0

Merge remote branch 'rmrector/configurable-north' into configurable-north

Conflicts:
	overviewer.py
	overviewer_core/world.py
This commit is contained in:
Aaron Griffith
2011-08-17 17:35:37 -04:00
15 changed files with 433 additions and 33 deletions

View File

@@ -195,6 +195,10 @@ 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.
--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.
@@ -264,6 +268,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

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']
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", helptext="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs)
parser.add_option("--display-config", dest="display_config", action="store_true", helptext="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) parser.add_option("--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)
@@ -219,6 +221,11 @@ def main():
optimizeimages.check_programs(optimizeimg) optimizeimages.check_programs(optimizeimg)
else: else:
optimizeimg = None optimizeimg = None
if options.north_direction:
north_direction = options.north_direction
else:
north_direction = 'lower-left'
logging.getLogger().setLevel( logging.getLogger().setLevel(
logging.getLogger().level + 10*options.quiet) logging.getLogger().level + 10*options.quiet)
@@ -233,7 +240,7 @@ 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)
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)))

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,13 +200,24 @@ 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
@@ -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

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

@@ -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,11 @@ 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="lower-left"):
self.worlddir = worlddir self.worlddir = worlddir
self.outputdir = outputdir self.outputdir = outputdir
self.useBiomeData = useBiomeData self.useBiomeData = useBiomeData
self.north_direction = 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
@@ -169,6 +170,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)
@@ -185,7 +198,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
@@ -201,7 +214,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)
@@ -219,9 +232,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
@@ -231,7 +255,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))
@@ -248,9 +272,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
@@ -296,6 +320,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
@@ -312,7 +346,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)
@@ -320,7 +367,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

@@ -152,6 +152,14 @@ 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"