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 diff --git a/README.rst b/README.rst index 942e4cb..0548053 100644 --- a/README.rst +++ b/README.rst @@ -195,6 +195,13 @@ 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. + 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 given below. @@ -228,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 @@ -264,6 +271,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/contrib/playerInspect.py b/contrib/playerInspect.py new file mode 100644 index 0000000..c520788 --- /dev/null +++ b/contrib/playerInspect.py @@ -0,0 +1,18 @@ +import sys + +sys.path.append(".") + +from overviewer_core.nbt import load +from overviewer_core import items + +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 " %-3d %s" % (item['Count'], items.id2item(item['id'])) + diff --git a/overviewer.py b/overviewer.py index cbd3f37..b0103b7 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', '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") @@ -116,6 +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. 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) @@ -152,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() @@ -219,6 +232,11 @@ def main(): optimizeimages.check_programs(optimizeimg) else: optimizeimg = None + + if options.north_direction: + north_direction = options.north_direction + else: + north_direction = 'auto' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) @@ -233,7 +251,17 @@ def main(): logging.info("Notice: Not using biome data for tinting") # First do world-level preprocessing - w = world.World(worlddir, destdir, useBiomeData=useBiomeData, regionlist=regionlist) + w = world.World(worlddir, destdir, useBiomeData=useBiomeData, regionlist=regionlist, north_direction=north_direction) + 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 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) + 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/chunk.py b/overviewer_core/chunk.py index b885d51..9dd38f2 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -73,18 +73,30 @@ def get_lvldata(world, filename, x, y, retries=2): 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.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) + return level['Blocks'] -def get_blockarray_fromfile(filename): +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) + d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] - return get_blockarray(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)), + rots) + return get_blockarray(chunk_data) 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.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)) + 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 @@ -97,7 +109,7 @@ 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.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)) + 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 @@ -106,7 +118,7 @@ def get_blocklight_array(level): 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.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)) + return level['Data'] def get_tileentity_data(level): """Returns the TileEntities TAG_List from chunk dat file""" diff --git a/overviewer_core/data/web_assets/compass.png b/overviewer_core/data/web_assets/compass_lower-left.png similarity index 100% rename from overviewer_core/data/web_assets/compass.png rename to overviewer_core/data/web_assets/compass_lower-left.png diff --git a/overviewer_core/data/web_assets/compass_lower-right.png b/overviewer_core/data/web_assets/compass_lower-right.png new file mode 100644 index 0000000..8990e7e Binary files /dev/null and b/overviewer_core/data/web_assets/compass_lower-right.png differ diff --git a/overviewer_core/data/web_assets/compass_upper-left.png b/overviewer_core/data/web_assets/compass_upper-left.png new file mode 100644 index 0000000..60e12d7 Binary files /dev/null and b/overviewer_core/data/web_assets/compass_upper-left.png differ diff --git a/overviewer_core/data/web_assets/compass_upper-right.png b/overviewer_core/data/web_assets/compass_upper-right.png new file mode 100644 index 0000000..6291365 Binary files /dev/null and b/overviewer_core/data/web_assets/compass_upper-right.png differ diff --git a/overviewer_core/data/web_assets/overviewer.js b/overviewer_core/data/web_assets/overviewer.js index 12ca796..cdf9339 100644 --- a/overviewer_core/data/web_assets/overviewer.js +++ b/overviewer_core/data/web_assets/overviewer.js @@ -536,6 +536,19 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.zoomLevels)); + 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; + y = -temp-1; + } + // This information about where the center column is may change with // a different drawing implementation -- check it again after any // drawing overhauls! @@ -607,6 +620,19 @@ var overviewer = { point.x += 64; point.z -= 64; + 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; + point.x = -temp; + } + return point; }, /** diff --git a/overviewer_core/data/web_assets/overviewerConfig.js b/overviewer_core/data/web_assets/overviewerConfig.js index bdb6015..c99398e 100644 --- a/overviewer_core/data/web_assets/overviewerConfig.js +++ b/overviewer_core/data/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' }, @@ -94,7 +94,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 diff --git a/overviewer_core/googlemap.py b/overviewer_core/googlemap.py index a4fb156..50ff422 100644 --- a/overviewer_core/googlemap.py +++ b/overviewer_core/googlemap.py @@ -77,6 +77,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', 'lower-left') if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") @@ -121,6 +122,8 @@ class MapGen(object): "{maxzoom}", str(zoomlevel)) config = config.replace( "{zoomlevels}", str(zoomlevel)) + config = config.replace( + "{north_direction}", self.north_direction) config = config.replace("{spawn_coords}", json.dumps(list(self.world.spawn))) @@ -157,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 @@ -172,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") @@ -182,11 +193,6 @@ class MapGen(object): output.write("\n") output.write("]);\n") - # save persistent data - self.world.persistentData['POI'] = self.world.POI - 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') 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 diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index aa30700..895eca9 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/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,24 @@ 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): + 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 @@ -318,21 +330,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 - for _ in xrange(32*32): - locations_append(self._read_chunk_location()) + 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) if closeFile: self.closefile() 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)) diff --git a/overviewer_core/rendernode.py b/overviewer_core/rendernode.py index 054e2ea..016e287 100644 --- a/overviewer_core/rendernode.py +++ b/overviewer_core/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(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/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; diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index e7057e7..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 8 +#define OVERVIEWER_EXTENSION_VERSION 10 /* 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); } diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index c6ad4a1..dc5fd69 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -513,6 +513,9 @@ def generate_texture_tuple(img, blockid): def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" + + data = convert_data(blockID, data) + # blocks need to be handled here (and in chunk.py) if blockID == 2: # grass @@ -1679,6 +1682,237 @@ def generate_special_texture(blockID, data): return None +def convert_data(blockID, data): + if blockID == 26: # bed + #Masked to not clobber block head/foot info + 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 == '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 == '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 == '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 == '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 == '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 + 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 == '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 == '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 == 'upper-left': + if data == 6: data = 7 + elif data == 7: data = 8 + elif data == 8: data = 6 + elif data == 9: data = 9 + 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 == '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 == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + 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 == '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 == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + 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 == '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 == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + 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 == '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 == 'upper-left': + data = (data + 4) % 16 + elif _north == 'upper-right': + data = (data + 8) % 16 + 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 == '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 == '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 == '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 == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + 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 == '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 == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + 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 == '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 == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + 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 == '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 == '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 == '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 == '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 == '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 == '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 == '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 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + + return data + def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) @@ -1722,8 +1956,25 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + biomeX = chunkX // 32 + biomeY = chunkY // 32 + rots = 0 + 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 + elif _north == 'lower-right': + temp = biomeX + biomeX = -biomeY-1 + biomeY = temp + rots = 1 - biomeFile = "b.%d.%d.biome" % (chunkX // 32, chunkY // 32) + biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) if biomeFile == currentBiomeFile: return currentBiomeData @@ -1733,7 +1984,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 @@ -1822,7 +2075,10 @@ biome_tall_fern_texture = None biome_leaf_texture = None specialblockmap = None -def generate(path=None,texture_size=24,bgc = (26,26,26,0)): +def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): + global _north + _north = north_direction + global _find_file_local_path global bgcolor bgcolor = bgc global _find_file_local_path, texture_dimensions diff --git a/overviewer_core/world.py b/overviewer_core/world.py index a18b86a..6c4c6f3 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -69,11 +69,70 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 - def __init__(self, worlddir, outputdir, useBiomeData=False,regionlist=None): + 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 + + # figure out chunk format is in use + # if not mcregion, error out early + data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data'] + #print data + if not ('version' in data and data['version'] == 19132): + logging.error("Sorry, This version of Minecraft-Overviewer only works with the new McRegion chunk format") + sys.exit(1) + + # stores Points Of Interest to be mapped with markers + # a list of dictionaries, see below for an example + self.POI = [] + + # if it exists, open overviewer.dat, and read in the data structure + # info self.persistentData. This dictionary can hold any information + # that may be needed between runs. + # Currently only holds into about POIs (more more details, see quadtree) + + 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: + self.persistentData = cPickle.load(p) + if not self.persistentData.get('north_direction', False): + # this is a pre-configurable-north map, so add the north_direction key + self.persistentData['north_direction'] = 'lower-left' + 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': + 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") @@ -96,43 +155,6 @@ class World(object): self.empty_chunk = [None,None] logging.debug("Done scanning regions") - # figure out chunk format is in use - # if not mcregion, error out early - data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data'] - #print data - if not ('version' in data and data['version'] == 19132): - logging.error("Sorry, This version of Minecraft-Overviewer only works with the new McRegion chunk format") - sys.exit(1) - - # stores Points Of Interest to be mapped with markers - # a list of dictionaries, see below for an example - self.POI = [] - - # if it exists, open overviewer.dat, and read in the data structure - # info self.persistentData. This dictionary can hold any information - # 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: - 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.pickleFile = os.path.join(self.outputdir, "overviewer.dat") - if os.path.exists(self.pickleFile): - with open(self.pickleFile,"rb") as p: - self.persistentData = cPickle.load(p) - else: - # some defaults - self.persistentData = dict(POI=[]) - def get_region_path(self, chunkX, chunkY): """Returns the path to the region that contains chunk (chunkX, chunkY) @@ -164,6 +186,18 @@ class World(object): data = nbt.read_all() level = data[1]['Level'] chunk_data = level + 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.array(numpy.rot90(numpy.frombuffer( + level['Data'], dtype=numpy.uint8).reshape((16,16,64)), + 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.array(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) @@ -180,7 +214,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 @@ -196,7 +230,7 @@ class World(object): 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) + # difference # change this function, and you MUST change unconvert_coords return (chunkx + chunky, chunky - chunkx) @@ -214,9 +248,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-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 + spawnZ = -temp ## The chunk that holds the spawn location chunkX = spawnX/16 @@ -226,7 +271,7 @@ class World(object): ## The filename of this chunk chunkFile = self.get_region_path(chunkX, chunkY) if chunkFile is not None: - data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1] + 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)) @@ -243,9 +288,9 @@ class World(object): except ChunkCorrupt: #ignore corrupt spawn, and continue pass - 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 @@ -291,6 +336,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 @@ -307,7 +362,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-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)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -315,7 +383,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-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(dirpath, f)) def get_save_dir(): """Returns the path to the local saves directory diff --git a/sample.settings.py b/sample.settings.py index 3b6aed2..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: @@ -152,10 +154,19 @@ verbose = 1 if "web_assets_hook" in locals(): skipjs = True +################################################################################ +### north_direction +## Make north point somewhere else! +## Valid options are 'lower-left', 'upper-left', 'upper-right', 'upper-left' +## default: lower-left +## Type: string +## Example: +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!") diff --git a/setup.py b/setup.py index a52880c..823b5d3 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', 'contribManager.py'] setup_kwargs['data_files'] = [('', doc_files)] setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures')