0

Merge remote-tracking branch 'upstream/master' into contribs

Conflicts:
	setup.py
This commit is contained in:
Andrew Chin
2011-08-24 21:00:27 -04:00
23 changed files with 783 additions and 92 deletions

View File

@@ -52,9 +52,11 @@ feature.
* Ryan Hitchman <hitchmanr@gmail.com> * Ryan Hitchman <hitchmanr@gmail.com>
* Jenny <jennytoo@gmail.com> * Jenny <jennytoo@gmail.com>
* Michael Jensen <emjay1988@gmail.com> * Michael Jensen <emjay1988@gmail.com>
* Maciej Małecki <maciej.malecki@hotmail.com> * Thomas Lake <tswsl1989@sucs.org>
* Maciej Malecki <maciej.malecki@hotmail.com>
* Ryan McCue <ryanmccue@cubegames.net> * Ryan McCue <ryanmccue@cubegames.net>
* Morlok8k <otis.spankmeyer@gmail.com> * Morlok8k <otis.spankmeyer@gmail.com>
* Ryan Rector <rmrector@gmail.com>
* Gregory Short <gshort2@gmail.com> * Gregory Short <gshort2@gmail.com>
* Sam Steele <sam@sigbox.c99.org> * Sam Steele <sam@sigbox.c99.org>
* timwolla <timwolla@mail.develfusion.com> * timwolla <timwolla@mail.develfusion.com>

View File

@@ -195,6 +195,13 @@ Options
--list-rendermodes --list-rendermodes
List the available render modes, and a short description of each. 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 --settings=PATH
Use this option to load settings from a file. The format of this file is Use this option to load settings from a file. The format of this file is
given below. given below.
@@ -228,8 +235,8 @@ zoom=ZOOM
This is equivalent to setting the dimensions of the highest zoom level. It 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 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, the map is rendered.* Setting this option too low *will crop your map.*
I know) (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 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 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 source. Overviewer looks in here for terrain.png and other textures before
it looks anywhere else. 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 Viewing the Results
------------------- -------------------
Within the output directory you will find two things: an index.html file, and a Within the output directory you will find two things: an index.html file, and a

18
contrib/playerInspect.py Normal file
View File

@@ -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']))

View File

@@ -96,6 +96,7 @@ def main():
cpus = 1 cpus = 1
avail_rendermodes = c_overviewer.get_render_modes() 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 = ConfigOptionParser(usage=helptext, config="settings.py")
parser.add_option("-V", "--version", dest="version", helptext="Displays version information and then exits", action="store_true") 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("-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("--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("--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("--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) #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) sys.exit(1)
worlddir = args[0] 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): if not os.path.exists(worlddir):
# world given is either world number, or name # world given is either world number, or name
worlds = world.get_worlds() worlds = world.get_worlds()
@@ -220,6 +233,11 @@ def main():
else: else:
optimizeimg = None optimizeimg = None
if options.north_direction:
north_direction = options.north_direction
else:
north_direction = 'auto'
logging.getLogger().setLevel( logging.getLogger().setLevel(
logging.getLogger().level + 10*options.quiet) logging.getLogger().level + 10*options.quiet)
logging.getLogger().setLevel( logging.getLogger().setLevel(
@@ -233,7 +251,17 @@ def main():
logging.info("Notice: Not using biome data for tinting") logging.info("Notice: Not using biome data for tinting")
# First do world-level preprocessing # 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) w.go(options.procs)
logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode))

View File

@@ -73,18 +73,30 @@ def get_lvldata(world, filename, x, y, retries=2):
def get_blockarray(level): def get_blockarray(level):
"""Takes the level struct as returned from get_lvldata, and returns the """Takes the level struct as returned from get_lvldata, and returns the
Block array, which just contains all the block ids""" 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""" """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'] 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): def get_skylight_array(level):
"""Returns the skylight array. This is 4 bits per block, but it is """Returns the skylight array. This is 4 bits per block, but it is
expanded for you so you may index it normally.""" 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 # this array is 2 blocks per byte, so expand it
skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
# Even elements get the lower 4 bits # 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 """Returns the blocklight array. This is 4 bits per block, but it
is expanded for you so you may index it normally.""" is expanded for you so you may index it normally."""
# expand just like get_skylight_array() # 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 = numpy.empty((16,16,128), dtype=numpy.uint8)
blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,::2] = blocklight & 0x0F
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
@@ -106,7 +118,7 @@ def get_blocklight_array(level):
def get_blockdata_array(level): def get_blockdata_array(level):
"""Returns the ancillary data from the 'Data' byte array. Data is packed """Returns the ancillary data from the 'Data' byte array. Data is packed
in a similar manner to skylight data""" 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): def get_tileentity_data(level):
"""Returns the TileEntities TAG_List from chunk dat file""" """Returns the TileEntities TAG_List from chunk dat file"""

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -536,6 +536,19 @@ var overviewer = {
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.zoomLevels)); 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 // This information about where the center column is may change with
// a different drawing implementation -- check it again after any // a different drawing implementation -- check it again after any
// drawing overhauls! // drawing overhauls!
@@ -607,6 +620,19 @@ var overviewer = {
point.x += 64; point.x += 64;
point.z -= 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; return point;
}, },
/** /**

View File

@@ -14,7 +14,7 @@ var overviewerConfig = {
'image': { 'image': {
'defaultMarker': 'signpost.png', 'defaultMarker': 'signpost.png',
'signMarker': 'signpost_icon.png', 'signMarker': 'signpost_icon.png',
'compass': 'compass.png', 'compass': 'compass_{north_direction}.png',
'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png', 'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png',
'queryMarker': 'http://google-maps-icons.googlecode.com/files/regroup.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 * 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. * 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 * Group definitions for objects that are partially selectable (signs and

View File

@@ -77,6 +77,7 @@ class MapGen(object):
self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_hook = configInfo.get('web_assets_hook', None)
self.web_assets_path = configInfo.get('web_assets_path', None) self.web_assets_path = configInfo.get('web_assets_path', None)
self.bg_color = configInfo.get('bg_color') self.bg_color = configInfo.get('bg_color')
self.north_direction = configInfo.get('north_direction', 'lower-left')
if not len(quadtrees) > 0: if not len(quadtrees) > 0:
raise ValueError("there must be at least one quadtree to work on") raise ValueError("there must be at least one quadtree to work on")
@@ -121,6 +122,8 @@ class MapGen(object):
"{maxzoom}", str(zoomlevel)) "{maxzoom}", str(zoomlevel))
config = config.replace( config = config.replace(
"{zoomlevels}", str(zoomlevel)) "{zoomlevels}", str(zoomlevel))
config = config.replace(
"{north_direction}", self.north_direction)
config = config.replace("{spawn_coords}", config = config.replace("{spawn_coords}",
json.dumps(list(self.world.spawn))) json.dumps(list(self.world.spawn)))
@@ -157,9 +160,6 @@ class MapGen(object):
def finalize(self): def finalize(self):
if self.skipjs:
return
# since we will only discover PointsOfInterest in chunks that need to be # since we will only discover PointsOfInterest in chunks that need to be
# [re]rendered, POIs like signs in unchanged chunks will not be listed # [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 # in self.world.POI. To make sure we don't remove these from markers.js
@@ -172,6 +172,17 @@ class MapGen(object):
else: else:
markers = self.world.POI 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 # write out the default marker table
with open(os.path.join(self.destdir, "markers.js"), 'w') as output: with open(os.path.join(self.destdir, "markers.js"), 'w') as output:
output.write("overviewer.collections.markerDatas.push([\n") output.write("overviewer.collections.markerDatas.push([\n")
@@ -182,11 +193,6 @@ class MapGen(object):
output.write("\n") output.write("\n")
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 # write out the default (empty, but documented) region table
with open(os.path.join(self.destdir, "regions.js"), 'w') as output: with open(os.path.join(self.destdir, "regions.js"), 'w') as output:
output.write('overviewer.collections.regionDatas.push([\n') output.write('overviewer.collections.regionDatas.push([\n')

211
overviewer_core/items.py Normal file
View File

@@ -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

View File

@@ -17,6 +17,7 @@ import gzip, zlib
import struct import struct
import StringIO import StringIO
import os import os
import numpy
# decorator to handle filename or object as first parameter # decorator to handle filename or object as first parameter
def _file_loader(func): def _file_loader(func):
@@ -34,15 +35,15 @@ def _file_loader(func):
def load(fileobj): def load(fileobj):
return NBTFileReader(fileobj).read_all() return NBTFileReader(fileobj).read_all()
def load_from_region(filename, x, y): def load_from_region(filename, x, y, north_direction):
nbt = load_region(filename).load_chunk(x, y) nbt = load_region(filename, north_direction).load_chunk(x, y)
if nbt is None: if nbt is None:
return None ## return none. I think this is who we should indicate missing chunks 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)) #raise IOError("No such chunk in region: (%i, %i)" % (x, y))
return nbt.read_all() return nbt.read_all()
def load_region(filename): def load_region(filename, north_direction):
return MCRFileReader(filename) return MCRFileReader(filename, north_direction)
# compile the unpacker's into a classes # compile the unpacker's into a classes
@@ -199,14 +200,25 @@ class MCRFileReader(object):
chunks (as instances of NBTFileReader), getting chunk timestamps, chunks (as instances of NBTFileReader), getting chunk timestamps,
and for listing chunks contained in the file.""" and for listing chunks contained in the file."""
def __init__(self, filename): def __init__(self, filename, north_direction):
self._file = None self._file = None
self._filename = filename self._filename = filename
self.north_direction = north_direction
# cache used when the entire header tables are read in get_chunks() # cache used when the entire header tables are read in get_chunks()
self._locations = None self._locations = None
self._timestamps = None self._timestamps = None
self._chunks = 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): def _read_24bit_int(self):
"""Read in a 24-bit, big-endian int, used in the chunk """Read in a 24-bit, big-endian int, used in the chunk
location table.""" location table."""
@@ -318,21 +330,24 @@ class MCRFileReader(object):
self.openfile() self.openfile()
self._chunks = None self._chunks = None
self._locations = [] self._locations = [0]*32*32
self._timestamps = [] self._timestamps = []
# go to the beginning of the file # go to the beginning of the file
self._file.seek(0) self._file.seek(0)
# read chunk location table # read chunk location table
locations_append = self._locations.append locations_index = numpy.reshape(numpy.rot90(numpy.reshape(range(32*32),
for _ in xrange(32*32): (32, 32)), -self.get_north_rotations()), -1)
locations_append(self._read_chunk_location()) for i in locations_index:
self._locations[i] = self._read_chunk_location()
# read chunk timestamp table # read chunk timestamp table
timestamp_append = self._timestamps.append timestamp_append = self._timestamps.append
for _ in xrange(32*32): for _ in xrange(32*32):
timestamp_append(self._read_chunk_timestamp()) 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: if closeFile:
self.closefile() self.closefile()

View File

@@ -302,7 +302,7 @@ class QuadtreeGen(object):
tile_mtime = None tile_mtime = None
#check mtimes on each part of the quad, this also checks if they exist #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 = [] quadPath_filtered = []
for path in quadPath: for path in quadPath:
try: try:

View File

@@ -65,7 +65,8 @@ def pool_initializer(rendernode):
# make sure textures are generated for this process # make sure textures are generated for this process
# and initialize c_overviewer # 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() c_overviewer.init_chunk_render()
# load biome data in each process, if needed # load biome data in each process, if needed

View File

@@ -194,7 +194,7 @@ alpha_over_full(PyObject *dest, PyObject *src, PyObject *mask, float overall_alp
} }
/* special cases */ /* special cases */
if (in_alpha == 255 || *outmask == 0) { if (in_alpha == 255 || (*outmask == 0 && in_alpha > 0)) {
*outmask = in_alpha; *outmask = in_alpha;
*out = *in; *out = *in;

View File

@@ -26,7 +26,7 @@
// increment this value if you've made a change to the c extesion // increment this value if you've made a change to the c extesion
// and want to force users to rebuild // and want to force users to rebuild
#define OVERVIEWER_EXTENSION_VERSION 8 #define OVERVIEWER_EXTENSION_VERSION 10
/* Python PIL, and numpy headers */ /* Python PIL, and numpy headers */
#include <Python.h> #include <Python.h>

View File

@@ -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 */ /* stairs and half-blocks take the skylevel from the upper block if it's transparent */
if (local_z != 127) { 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)) { 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 { } else {
upper_block = 0; upper_block = 0;
@@ -195,7 +200,7 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state,
} }
/* the block has a bad blocklevel, estimate it from neigborhood /* 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); blocklevel = estimate_blocklevel(self, state, x, y, z, NULL);
} }

View File

@@ -513,6 +513,9 @@ def generate_texture_tuple(img, blockid):
def generate_special_texture(blockID, data): def generate_special_texture(blockID, data):
"""Generates a special texture, such as a correctly facing minecraft track""" """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) # blocks need to be handled here (and in chunk.py)
if blockID == 2: # grass if blockID == 2: # grass
@@ -1679,6 +1682,237 @@ def generate_special_texture(blockID, data):
return None 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): def tintTexture(im, c):
# apparently converting to grayscale drops the alpha channel? # apparently converting to grayscale drops the alpha channel?
i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c)
@@ -1722,8 +1956,25 @@ def getBiomeData(worlddir, chunkX, chunkY):
''' '''
global currentBiomeFile, currentBiomeData 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: if biomeFile == currentBiomeFile:
return currentBiomeData return currentBiomeData
@@ -1733,7 +1984,9 @@ def getBiomeData(worlddir, chunkX, chunkY):
# make sure the file size is correct # make sure the file size is correct
if not len(rawdata) == 512 * 512 * 2: if not len(rawdata) == 512 * 512 * 2:
raise Exception("Biome file %s is not valid." % (biomeFile,)) 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: except IOError:
data = None data = None
pass # no biome data pass # no biome data
@@ -1822,7 +2075,10 @@ biome_tall_fern_texture = None
biome_leaf_texture = None biome_leaf_texture = None
specialblockmap = 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 global bgcolor
bgcolor = bgc bgcolor = bgc
global _find_file_local_path, texture_dimensions global _find_file_local_path, texture_dimensions

View File

@@ -69,10 +69,69 @@ class World(object):
mincol = maxcol = minrow = maxrow = 0 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.worlddir = worlddir
self.outputdir = outputdir self.outputdir = outputdir
self.useBiomeData = useBiomeData 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 #find region files, or load the region list
#this also caches all the region file header info #this also caches all the region file header info
@@ -96,43 +155,6 @@ class World(object):
self.empty_chunk = [None,None] self.empty_chunk = [None,None]
logging.debug("Done scanning regions") 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): def get_region_path(self, chunkX, chunkY):
"""Returns the path to the region that contains chunk (chunkX, chunkY) """Returns the path to the region that contains chunk (chunkX, chunkY)
@@ -164,6 +186,18 @@ class World(object):
data = nbt.read_all() data = nbt.read_all()
level = data[1]['Level'] level = data[1]['Level']
chunk_data = 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 = {}
#chunk_data['skylight'] = chunk.get_skylight_array(level) #chunk_data['skylight'] = chunk.get_skylight_array(level)
#chunk_data['blocklight'] = chunk.get_blocklight_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: if self.regions.get(filename) is not None:
self.regions[filename][0].closefile() self.regions[filename][0].closefile()
chunkcache = {} chunkcache = {}
mcr = nbt.MCRFileReader(filename) mcr = nbt.MCRFileReader(filename, self.north_direction)
self.regions[filename] = (mcr,os.path.getmtime(filename),chunkcache) self.regions[filename] = (mcr,os.path.getmtime(filename),chunkcache)
return mcr return mcr
@@ -196,7 +230,7 @@ class World(object):
in the image each one should be. Returns (col, row).""" in the image each one should be. Returns (col, row)."""
# columns are determined by the sum of the chunk coords, rows are the # 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 # change this function, and you MUST change unconvert_coords
return (chunkx + chunky, chunky - chunkx) return (chunkx + chunky, chunky - chunkx)
@@ -214,9 +248,20 @@ class World(object):
## read spawn info from level.dat ## read spawn info from level.dat
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1] 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'] 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 ## The chunk that holds the spawn location
chunkX = spawnX/16 chunkX = spawnX/16
@@ -226,7 +271,7 @@ class World(object):
## The filename of this chunk ## The filename of this chunk
chunkFile = self.get_region_path(chunkX, chunkY) chunkFile = self.get_region_path(chunkX, chunkY)
if chunkFile is not None: 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: if data is not None:
level = data['Level'] level = data['Level']
blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
@@ -243,9 +288,9 @@ class World(object):
except ChunkCorrupt: except ChunkCorrupt:
#ignore corrupt spawn, and continue #ignore corrupt spawn, and continue
pass 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))) msg="Spawn", type="spawn", chunk=(chunkX, chunkY)))
self.spawn = (spawnX, spawnY, spawnZ) self.spawn = (disp_spawnX, spawnY, disp_spawnZ)
def go(self, procs): def go(self, procs):
"""Scan the world directory, to fill in """Scan the world directory, to fill in
@@ -291,6 +336,16 @@ class World(object):
self.findTrueSpawn() 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): def _iterate_regionfiles(self,regionlist=None):
"""Returns an iterator of all of the region files, along with their """Returns an iterator of all of the region files, along with their
coordinates coordinates
@@ -307,7 +362,20 @@ class World(object):
if f.startswith("r.") and f.endswith(".mcr"): if f.startswith("r.") and f.endswith(".mcr"):
p = f.split(".") p = f.split(".")
logging.debug("Using path %s from regionlist", f) 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: else:
logging.warning("Ignore path '%s' in regionlist", f) 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"): for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"):
dirpath, f = os.path.split(path) dirpath, f = os.path.split(path)
p = f.split(".") 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(): def get_save_dir():
"""Returns the path to the local saves directory """Returns the path to the local saves directory

View File

@@ -33,7 +33,9 @@ if procs < 1: procs = 1
## Sets the zoom level manually instead of calculating it. This can be useful ## 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 ## if you have outlier chunks that make your world too big. This value will
## make the highest zoom level contain (2**ZOOM)^2 tiles ## 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 ## Default: Automatically calculated from your world
## Type: integer ## Type: integer
## Example: ## Example:
@@ -152,10 +154,19 @@ verbose = 1
if "web_assets_hook" in locals(): if "web_assets_hook" in locals():
skipjs = True 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 ### As a reminder, *don't use this file verbatim*, it should only be used as
### a guide. ### a guide. Be sure to read what each option does before you set it.
### See the README for more details.
import sys import sys
sys.exit("This sample-settings file shouldn't be used directly!") sys.exit("This sample-settings file shouldn't be used directly!")

View File

@@ -91,6 +91,10 @@ def recursive_package_data(src, package_dir='overviewer_core'):
# #
if py2exe is not None: 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['console'] = ['overviewer.py', 'contribManager.py']
setup_kwargs['data_files'] = [('', doc_files)] setup_kwargs['data_files'] = [('', doc_files)]
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures') setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures')