def main(): """Creates a specified region and downloads files from USGS.""" # example: # ./GetRegion.py --name BlockIsland --ymax 41.2378 --ymin 41.1415 --xmin -71.6202 --xmax -71.5332 # defaults default_orthoIDs = ','.join(Region.productIDs['ortho']) default_elevationIDs = ','.join(Region.productIDs['elevation']) default_landcoverIDs = ','.join(Region.productIDs['landcover']) # parse options and get results parser = argparse.ArgumentParser(description='Create regions and download files from USGS.') parser.add_argument('--name', required=True, type=str, help='name of the region to be generated') parser.add_argument('--xmax', required=True, type=float, help='easternmost longitude (west is negative)') parser.add_argument('--xmin', required=True, type=float, help='westernmost longitude (west is negative)') parser.add_argument('--ymax', required=True, type=float, help='northernmost latitude (south is negative)') parser.add_argument('--ymin', required=True, type=float, help='southernmost longitude (south is negative)') parser.add_argument('--tilesize', type=int, help='tilesize value (default %d)' % Region.tilesize) parser.add_argument('--scale', type=int, help='scale value (default %d)' % Region.scale) parser.add_argument('--vscale', type=int, help='vscale value (default %d)' % Region.vscale) parser.add_argument('--trim', type=int, help='trim value (default %d)' % Region.trim) parser.add_argument('--sealevel', type=int, help='sealevel value (default %d)' % Region.sealevel) parser.add_argument('--maxdepth', type=int, help='maxdepth value (default %d)' % Region.maxdepth) parser.add_argument('--orthoIDs', default=default_orthoIDs, type=checkOrthoIDs, help='ordered list of product IDs (default %s)' % default_orthoIDs) parser.add_argument('--elevationIDs', default=default_elevationIDs, type=checkElevationIDs, help='ordered list of product IDs (default %s)' % default_elevationIDs) parser.add_argument('--landcoverIDs', default=default_landcoverIDs, type=checkLandcoverIDs, help='ordered list of product IDs (default %s)' % default_landcoverIDs) parser.add_argument('--enable-ore', action='store_true', dest='doOre', default=False, help='enable ore generation') parser.add_argument('--enable-schematics', action='store_true', dest='doSchematics', default=False, help='enable schematic usage') parser.add_argument("-v", "--verbosity", action="count", \ help="increase output verbosity") parser.add_argument("-q", "--quiet", action="store_true", \ help="suppress informational output") args = parser.parse_args() # set up logging log_level = klog_levels.LOG_INFO if args.quiet: log_level = klog_levels.LOG_ERROR if args.verbosity: # v=1 is DEBUG 1, v=2 is DEBUG 2, and so on log_level += args.verbosity log = klogger(log_level) # create the region log.log_info("Creating new region %s..." % args.name) myRegion = Region(name=args.name, xmax=args.xmax, xmin=args.xmin, ymax=args.ymax, ymin=args.ymin, scale=args.scale, vscale=args.vscale, trim=args.trim, tilesize=args.tilesize, sealevel=args.sealevel, maxdepth=args.maxdepth, oiIDs=args.orthoIDs, lcIDs=args.landcoverIDs, elIDs=args.elevationIDs, doOre=args.doOre, doSchematics=args.doSchematics) log.log_info("Retrieving files...") myRegion.log = log myRegion.getfiles()
def main(): # parse options and get results parser = argparse.ArgumentParser( description= 'Converts a single building from a Collada file and pastes into a Minecraft world' ) parser.add_argument('--model', required=True, type=str, \ help='relative or absolute path to .kmz file containing Collada model and assets') parser.add_argument('--world', required=True, type=str, \ help='path to main folder of a target Minecraft world') parser.add_argument("-v", "--verbosity", action="count", \ help="increase output verbosity") parser.add_argument("-q", "--quiet", action="store_true", \ help="suppress informational output") args = parser.parse_args() # set up logging log_level = klog_levels.LOG_INFO if args.quiet: log_level = klog_levels.LOG_ERROR if args.verbosity: # v=1 is DEBUG 1, v=2 is DEBUG 2, and so on log_level += args.verbosity log = klogger(log_level) # Name of the model that we'll be processing filename = args.model log.log_info("Converting %s and placing into %s" % \ (os.path.basename(filename), os.path.basename(args.world))) # Determine where to paste into target world zipf = zipfile.ZipFile(args.model, 'r') kmldata = minidom.parse(zipf.open('doc.kml')) zipf = None # Determine location information location = kmldata.getElementsByTagName('Location')[0] latitude = float( location.getElementsByTagName('latitude')[0].childNodes[0].data) longitude = float( location.getElementsByTagName('longitude')[0].childNodes[0].data) altmode = str( kmldata.getElementsByTagName('altitudeMode')[0].childNodes[0].data) altitude = float( location.getElementsByTagName('altitude')[0].childNodes[0].data) # Determine orientation information orientation = kmldata.getElementsByTagName('Orientation')[0] heading = float( orientation.getElementsByTagName('heading')[0].childNodes[0].data) kmldata = None if abs(heading) > 1.0: log.log_fatal("Model specifies heading of %f, but this script does" \ " not support model rotation" % heading) # Get information about the target world yamlfile = open(os.path.join(args.world, 'Region.yaml'), 'r') yamlfile.readline() # discard first line myRegion = yaml.safe_load(yamlfile) yamlfile.close() # Check important things if myRegion["scale"] != 1 or myRegion["vscale"] != 1: log.log_fatal("Currently only scale=1 and vscale=1 are allowed") # Compute the world utm (x,y) for this model. Oddly enough, we can use these # coordinates directly (for the 1:1 scale case. This script just handles that) llextents = myRegion['wgs84extents']['elevation'] easting, northing, utmzone, utmletter = utmll.from_latlon( latitude, longitude) northing = (myRegion['tiles']['ymin'] + myRegion['tiles']['ymax']) * myRegion['tilesize'] \ - northing log.log_debug(1, "Base easting = %d, northing = %d in UTM Zone %d%s" % \ (easting, northing, utmzone, utmletter)) modelBaseLoc = [easting, northing, 0] log.log_debug(1,"Loc: %.10f,%.10f => %d,%d within %s" % \ (latitude, longitude, modelBaseLoc[0], modelBaseLoc[1], str(llextents))) # Open the model and determine its extents model = collada.Collada( filename, ignore=[collada.DaeUnsupportedError, collada.DaeBrokenRefError]) maxs = array([-1e99, -1e99, -1e99]) mins = array([1e99, 1e99, 1e99]) mr = ModelRecurse(log) mins, maxs = mr.recurse_model(model, "extents", [mins, maxs]) log.log_info("Computed model extents: [%f, %f, %f,] to [%f, %f, %f]" % (mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2])) # some sort of scaling information scale = [.01, .01, .01] if model.assetInfo != None and model.assetInfo.unitmeter != None: log.log_debug(1,"This model contains units, %f %s per meter" % \ (model.assetInfo.unitmeter, model.assetInfo.unitname)) scale = model.assetInfo.unitmeter scale = [scale, scale, scale] t2v = Tri2Voxel(model, log) t2v.scale = array(scale) t2v.geom_prep(mins, maxs) # Use extents and modelBaseLoc to compute the world coordinate that # corresponds to the output array's [0,0,0] #cornerBase = t2v.tvoffset[0] * t2v.tvscale[0] cornerBase = np.multiply(t2v.scale, array([-mins[0], maxs[1], 0])) modelBaseLoc -= cornerBase modelBaseLoc = [round(x) for x in modelBaseLoc] log.log_debug(2,"cornerBase is %s, yielding modelBaseLoc of %s" % \ (str(cornerBase), str(modelBaseLoc))) # Convert mr.recurse_model(model, "convert", t2v) # Do the conversion! # Fix orientation t2v.arr3d_id = np.fliplr(t2v.arr3d_id) # Fix block ID array t2v.arr3d_dt = np.fliplr(t2v.arr3d_dt) # Fix damage val array # Print some stats ar1 = np.count_nonzero(t2v.arr3d_id) ar01 = np.prod(t2v.arrdim) log.log_info("%d/%d voxels filled (%.2f%% fill level)" % (ar1, ar01, 100 * ar1 / ar01)) log.log_info("t2v reports %d voxels changed" % t2v.voxchg) # Compute world-scaled altitude information # This must be done after the level height is adjusted, otherwise one of the # (loaded, cached) chunks will have an incorrect height. if altmode == "absolute": sealevel = myRegion['sealevel'] if 'sealevel' in myRegion else 64 modelAltBase = int(altitude * myRegion['vscale'] + sealevel) elif altmode == "relativeToGround": level = mclevel.fromFile(os.path.join(args.world, "level.dat")) xbase = int(round(modelBaseLoc[0] + cornerBase[0])) zbase = int(round(modelBaseLoc[1] + cornerBase[1])) chunk = level.getChunk(int(xbase / 16.), int(zbase / 16.)) voxcol = chunk.Blocks[xbase % 16, zbase % 16, :] voxtop = [i for i, e in enumerate(voxcol) if e != 0][-1] + 1 modelAltBase = int(voxtop + modelBaseLoc[2]) chunk = None level.close() level = None else: log.log_fatal("Unknown altitude mode in KML file.") log.log_info("Model base altitude is %d meters (voxels)" % modelAltBase) # Compute new world height worldheight = int(modelAltBase + t2v.arrdim[2]) worldheight |= worldheight >> 1 worldheight |= worldheight >> 2 worldheight |= worldheight >> 4 worldheight |= worldheight >> 8 worldheight |= worldheight >> 16 worldheight += 1 # Open MC level for computation level = mclevel.fromFile(os.path.join(args.world, "level.dat")) if worldheight > level.Height: log.log_info("World height increased from %d to %d meters" % \ (level.Height,worldheight)) level.Height = worldheight level.root_tag["Data"]["worldHeight"] = nbt.TAG_Int(worldheight) else: log.log_info("World height unmodified at %d meters" % worldheight) # Figure out what chunks will be modified chunksx = [int(np.floor(modelBaseLoc[0]/16.)), \ int(np.floor((modelBaseLoc[0]+t2v.arrdim[0])/16.))] chunksz = [int(np.floor(modelBaseLoc[1]/16.)), \ int(np.floor((modelBaseLoc[1]+t2v.arrdim[1])/16.))] # Modify the chunks with new building data for x in xrange(chunksx[0], 1 + chunksx[1]): for z in xrange(chunksz[0], 1 + chunksz[1]): # Chunk sub-selection chunk = level.getChunk(x, z) xmin = max(0, modelBaseLoc[0] - 16 * x) xmax = min(16, t2v.arrdim[0] + modelBaseLoc[0] - 16 * x) zmin = max(0, modelBaseLoc[1] - 16 * z) zmax = min(16, t2v.arrdim[1] + modelBaseLoc[1] - 16 * z) # Model sub-selection mxmin = (16 * x) + xmin - modelBaseLoc[0] mzmin = (16 * z) + zmin - modelBaseLoc[1] log.log_debug(2,"Copying region %d,%d,%d to %d,%d,%d" % \ (xmin,modelAltBase,zmin,xmax,(modelAltBase+t2v.arrdim[2]),zmax)) log.log_debug(2,"From model %d,%d,%d to %d,%d,%d" % \ (mxmin,0,mzmin,mxmin+(xmax-xmin),t2v.arrdim[2],mzmin+(zmax-zmin))) if xmax <= 0 or zmax <= 0: log.log_debug(1, "Skipping out-of-bounds copy") continue # Checking to make sure numpy isn't going to pitch a fit shapes = [ t2v.arrdim[2], chunk.Data[xmin, zmin, modelAltBase:(modelAltBase + t2v.arrdim[2])].shape[0] ] if shapes[0] != shapes[1]: log.log_fatal("Cannot store resulting model. Chunk (%d,%d) selected height %d does not match " \ "model matrix height %d" % (x, z, shapes[0], shapes[1])) inp = chunk.Blocks[xmin:xmax,zmin:zmax, \ modelAltBase:(modelAltBase+t2v.arrdim[2])] # Data first because Blocks must retain its 0s ind = chunk.Data[xmin:xmax,zmin:zmax, \ modelAltBase:(modelAltBase+t2v.arrdim[2])] chunk.Data[xmin:xmax,zmin:zmax, \ modelAltBase:(modelAltBase+t2v.arrdim[2])] = \ np.where(inp != 0, ind, \ t2v.arr3d_dt[mxmin:mxmin + (xmax-xmin), mzmin:mzmin + (zmax-zmin), :]) # Blocks second. chunk.Blocks[xmin:xmax,zmin:zmax, \ modelAltBase:(modelAltBase+t2v.arrdim[2])] = \ np.where(inp != 0, inp, \ t2v.arr3d_id[mxmin:mxmin + (xmax-xmin), mzmin:mzmin + (zmax-zmin), :]) # And mark the chunk. chunk.chunkChanged() log.log_info("Relighting level...") level.generateLights() log.log_info("Saving level...") level.saveInPlace()
def main(): # parse options and get results parser = argparse.ArgumentParser( description="Converts a single building from a Collada file and pastes into a Minecraft world" ) parser.add_argument( "--model", required=True, type=str, help="relative or absolute path to .kmz file containing Collada model and assets", ) parser.add_argument("--world", required=True, type=str, help="path to main folder of a target Minecraft world") parser.add_argument("-v", "--verbosity", action="count", help="increase output verbosity") parser.add_argument("-q", "--quiet", action="store_true", help="suppress informational output") args = parser.parse_args() # set up logging log_level = klog_levels.LOG_INFO if args.quiet: log_level = klog_levels.LOG_ERROR if args.verbosity: # v=1 is DEBUG 1, v=2 is DEBUG 2, and so on log_level += args.verbosity log = klogger(log_level) # Name of the model that we'll be processing filename = args.model log.log_info("Converting %s and placing into %s" % (os.path.basename(filename), os.path.basename(args.world))) # Determine where to paste into target world zipf = zipfile.ZipFile(args.model, "r") kmldata = minidom.parse(zipf.open("doc.kml")) zipf = None # Determine location information location = kmldata.getElementsByTagName("Location")[0] latitude = float(location.getElementsByTagName("latitude")[0].childNodes[0].data) longitude = float(location.getElementsByTagName("longitude")[0].childNodes[0].data) altmode = str(kmldata.getElementsByTagName("altitudeMode")[0].childNodes[0].data) altitude = float(location.getElementsByTagName("altitude")[0].childNodes[0].data) # Determine orientation information orientation = kmldata.getElementsByTagName("Orientation")[0] heading = float(orientation.getElementsByTagName("heading")[0].childNodes[0].data) kmldata = None if abs(heading) > 1.0: log.log_fatal("Model specifies heading of %f, but this script does" " not support model rotation" % heading) # Get information about the target world yamlfile = open(os.path.join(args.world, "Region.yaml"), "r") yamlfile.readline() # discard first line myRegion = yaml.safe_load(yamlfile) yamlfile.close() # Check important things if myRegion["scale"] != 1 or myRegion["vscale"] != 1: log.log_fatal("Currently only scale=1 and vscale=1 are allowed") # Compute the world utm (x,y) for this model. Oddly enough, we can use these # coordinates directly (for the 1:1 scale case. This script just handles that) llextents = myRegion["wgs84extents"]["elevation"] easting, northing, utmzone, utmletter = utmll.from_latlon(latitude, longitude) northing = (myRegion["tiles"]["ymin"] + myRegion["tiles"]["ymax"]) * myRegion["tilesize"] - northing log.log_debug(1, "Base easting = %d, northing = %d in UTM Zone %d%s" % (easting, northing, utmzone, utmletter)) modelBaseLoc = [easting, northing, 0] log.log_debug( 1, "Loc: %.10f,%.10f => %d,%d within %s" % (latitude, longitude, modelBaseLoc[0], modelBaseLoc[1], str(llextents)), ) # Open the model and determine its extents model = collada.Collada(filename, ignore=[collada.DaeUnsupportedError, collada.DaeBrokenRefError]) maxs = array([-1e99, -1e99, -1e99]) mins = array([1e99, 1e99, 1e99]) mr = ModelRecurse(log) mins, maxs = mr.recurse_model(model, "extents", [mins, maxs]) log.log_info( "Computed model extents: [%f, %f, %f,] to [%f, %f, %f]" % (mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]) ) # some sort of scaling information scale = [0.01, 0.01, 0.01] if model.assetInfo != None and model.assetInfo.unitmeter != None: log.log_debug( 1, "This model contains units, %f %s per meter" % (model.assetInfo.unitmeter, model.assetInfo.unitname) ) scale = model.assetInfo.unitmeter scale = [scale, scale, scale] t2v = Tri2Voxel(model, log) t2v.scale = array(scale) t2v.geom_prep(mins, maxs) # Use extents and modelBaseLoc to compute the world coordinate that # corresponds to the output array's [0,0,0] # cornerBase = t2v.tvoffset[0] * t2v.tvscale[0] cornerBase = np.multiply(t2v.scale, array([-mins[0], maxs[1], 0])) modelBaseLoc -= cornerBase modelBaseLoc = [round(x) for x in modelBaseLoc] log.log_debug(2, "cornerBase is %s, yielding modelBaseLoc of %s" % (str(cornerBase), str(modelBaseLoc))) # Convert mr.recurse_model(model, "convert", t2v) # Do the conversion! # Fix orientation t2v.arr3d_id = np.fliplr(t2v.arr3d_id) # Fix block ID array t2v.arr3d_dt = np.fliplr(t2v.arr3d_dt) # Fix damage val array # Print some stats ar1 = np.count_nonzero(t2v.arr3d_id) ar01 = np.prod(t2v.arrdim) log.log_info("%d/%d voxels filled (%.2f%% fill level)" % (ar1, ar01, 100 * ar1 / ar01)) log.log_info("t2v reports %d voxels changed" % t2v.voxchg) # Compute world-scaled altitude information # This must be done after the level height is adjusted, otherwise one of the # (loaded, cached) chunks will have an incorrect height. if altmode == "absolute": sealevel = myRegion["sealevel"] if "sealevel" in myRegion else 64 modelAltBase = int(altitude * myRegion["vscale"] + sealevel) elif altmode == "relativeToGround": level = mclevel.fromFile(os.path.join(args.world, "level.dat")) xbase = int(round(modelBaseLoc[0] + cornerBase[0])) zbase = int(round(modelBaseLoc[1] + cornerBase[1])) chunk = level.getChunk(int(xbase / 16.0), int(zbase / 16.0)) voxcol = chunk.Blocks[xbase % 16, zbase % 16, :] voxtop = [i for i, e in enumerate(voxcol) if e != 0][-1] + 1 modelAltBase = int(voxtop + modelBaseLoc[2]) chunk = None level.close() level = None else: log.log_fatal("Unknown altitude mode in KML file.") log.log_info("Model base altitude is %d meters (voxels)" % modelAltBase) # Compute new world height worldheight = int(modelAltBase + t2v.arrdim[2]) worldheight |= worldheight >> 1 worldheight |= worldheight >> 2 worldheight |= worldheight >> 4 worldheight |= worldheight >> 8 worldheight |= worldheight >> 16 worldheight += 1 # Open MC level for computation level = mclevel.fromFile(os.path.join(args.world, "level.dat")) if worldheight > level.Height: log.log_info("World height increased from %d to %d meters" % (level.Height, worldheight)) level.Height = worldheight level.root_tag["Data"]["worldHeight"] = nbt.TAG_Int(worldheight) else: log.log_info("World height unmodified at %d meters" % worldheight) # Figure out what chunks will be modified chunksx = [int(np.floor(modelBaseLoc[0] / 16.0)), int(np.floor((modelBaseLoc[0] + t2v.arrdim[0]) / 16.0))] chunksz = [int(np.floor(modelBaseLoc[1] / 16.0)), int(np.floor((modelBaseLoc[1] + t2v.arrdim[1]) / 16.0))] # Modify the chunks with new building data for x in xrange(chunksx[0], 1 + chunksx[1]): for z in xrange(chunksz[0], 1 + chunksz[1]): # Chunk sub-selection chunk = level.getChunk(x, z) xmin = max(0, modelBaseLoc[0] - 16 * x) xmax = min(16, t2v.arrdim[0] + modelBaseLoc[0] - 16 * x) zmin = max(0, modelBaseLoc[1] - 16 * z) zmax = min(16, t2v.arrdim[1] + modelBaseLoc[1] - 16 * z) # Model sub-selection mxmin = (16 * x) + xmin - modelBaseLoc[0] mzmin = (16 * z) + zmin - modelBaseLoc[1] log.log_debug( 2, "Copying region %d,%d,%d to %d,%d,%d" % (xmin, modelAltBase, zmin, xmax, (modelAltBase + t2v.arrdim[2]), zmax), ) log.log_debug( 2, "From model %d,%d,%d to %d,%d,%d" % (mxmin, 0, mzmin, mxmin + (xmax - xmin), t2v.arrdim[2], mzmin + (zmax - zmin)), ) if xmax <= 0 or zmax <= 0: log.log_debug(1, "Skipping out-of-bounds copy") continue # Checking to make sure numpy isn't going to pitch a fit shapes = [t2v.arrdim[2], chunk.Data[xmin, zmin, modelAltBase : (modelAltBase + t2v.arrdim[2])].shape[0]] if shapes[0] != shapes[1]: log.log_fatal( "Cannot store resulting model. Chunk (%d,%d) selected height %d does not match " "model matrix height %d" % (x, z, shapes[0], shapes[1]) ) inp = chunk.Blocks[xmin:xmax, zmin:zmax, modelAltBase : (modelAltBase + t2v.arrdim[2])] # Data first because Blocks must retain its 0s ind = chunk.Data[xmin:xmax, zmin:zmax, modelAltBase : (modelAltBase + t2v.arrdim[2])] chunk.Data[xmin:xmax, zmin:zmax, modelAltBase : (modelAltBase + t2v.arrdim[2])] = np.where( inp != 0, ind, t2v.arr3d_dt[mxmin : mxmin + (xmax - xmin), mzmin : mzmin + (zmax - zmin), :] ) # Blocks second. chunk.Blocks[xmin:xmax, zmin:zmax, modelAltBase : (modelAltBase + t2v.arrdim[2])] = np.where( inp != 0, inp, t2v.arr3d_id[mxmin : mxmin + (xmax - xmin), mzmin : mzmin + (zmax - zmin), :] ) # And mark the chunk. chunk.chunkChanged() log.log_info("Relighting level...") level.generateLights() log.log_info("Saving level...") level.saveInPlace()
def main(): """Builds a region.""" # example: # ./BuildRegion.py --name BlockIsland # parse options and get results parser = argparse.ArgumentParser(description='Builds Minecraft worlds from regions.') parser.add_argument('--name', required=True, type=str, \ help='name of the region to be built') parser.add_argument('--single', action='store_true', \ help='enable single-threaded mode for debugging or profiling') parser.add_argument('--safemerge', action='store_true', \ help='use \"safer\" method of merging tiles together') parser.add_argument("-v", "--verbosity", action="count", \ help="increase output verbosity") parser.add_argument("-q", "--quiet", action="store_true", \ help="suppress informational output") args = parser.parse_args() # set up logging log_level = klog_levels.LOG_INFO if args.quiet: log_level = klog_levels.LOG_ERROR if args.verbosity: # v=1 is DEBUG 1, v=2 is DEBUG 2, and so on log_level += args.verbosity log = klogger(log_level) # build the region log.log_info("Building region %s..." % args.name) yamlfile = file(os.path.join('Regions', args.name, 'Region.yaml')) myRegion = yaml.load(yamlfile) yamlfile.close() # exit if map does not exist if not os.path.exists(myRegion.mapname): log.log_fatal("No map file exists!") # tree and ore variables treeobjs = dict([(tree.name, tree) for tree in treeObjs]) trees = dict([(name, list()) for name in treeobjs]) oreobjs = dict([(ore.name, ore) for ore in oreObjs]) ores = dict([(name, list()) for name in oreobjs]) # generate overall world worlddir = os.path.join('Worlds', args.name) world = mclevel.MCInfdevOldLevel(worlddir, create=True) peak = [0, 0, 0] save(world) world = None # generate individual tiles tilexrange = xrange(myRegion.tiles['xmin'], myRegion.tiles['xmax']) tileyrange = xrange(myRegion.tiles['ymin'], myRegion.tiles['ymax']) name = myRegion.name tiles = [(log, name, x, y) for x, y in product(tilexrange, tileyrange)] if args.single: # single process version log.log_warn("Single-threaded region merge") for tile in tiles: buildtile(tile) else: # multi-process version pool = Pool() rs = pool.map_async(buildtile, tiles) pool.close() while not(rs.ready()): remaining = rs._number_left log.log_info("Waiting for %s buildtile tasks to complete..." % remaining) time.sleep(10) pool.join() # Just as a precaution. # Necessary for tile-welding -> regions cleanmkdir(worlddir) cleanmkdir(os.path.join(worlddir, 'region')) # Generate regions if not(args.safemerge): regionsize = 32 * 16 regionxrange = xrange(int(floor(myRegion.tiles['xmin'] * (myRegion.tilesize / float(regionsize)))), \ int(ceil(myRegion.tiles['xmax'] * (myRegion.tilesize / float(regionsize))))) regionyrange = xrange(int(floor(myRegion.tiles['ymin'] * (myRegion.tilesize / float(regionsize)))), \ int(ceil(myRegion.tiles['ymax'] * (myRegion.tilesize / float(regionsize))))) regions = [(log, name, x, y) for x, y in product(regionxrange, regionyrange)] # merge individual tiles into regions log.log_info("Merging %d tiles into one world..." % len(tiles)) for tile in tiles: (dummy, name, x, y) = tile tiledir = os.path.join('Regions', name, 'Tiles', '%dx%d' % (x, y)) if not(os.path.isfile(os.path.join(tiledir, 'Tile.yaml'))): log.log_fatal("The following tile is missing. Please re-run this script:\n%s" % \ os.path.join(tiledir, 'Tile.yaml')) if args.single: # single process version log.log_warn("Single-threaded region merge") for region in regions: buildregion(region) else: # multi-process version pool = Pool() rs = pool.map_async(buildregion, regions) pool.close() while not(rs.ready()): remaining = rs._number_left log.log_info("Waiting for %s buildregion tasks to complete..." % remaining) time.sleep(10) pool.join() # Just as a precaution. world = mclevel.MCInfdevOldLevel(worlddir, create=True) if not(args.safemerge): mcoffsetx = myRegion.tiles['xmin'] * myRegion.tilesize mcoffsetz = myRegion.tiles['ymin'] * myRegion.tilesize mcsizex = (myRegion.tiles['xmax'] - myRegion.tiles['xmin']) * myRegion.tilesize mcsizez = (myRegion.tiles['ymax'] - myRegion.tiles['ymin']) * myRegion.tilesize tilebox = box.BoundingBox((mcoffsetx, 0, mcoffsetz), (mcsizex, world.Height, mcsizez)) world.createChunksInBox(tilebox) for tile in tiles: (dummy, name, x, y) = tile tiledir = os.path.join('Regions', name, 'Tiles', '%dx%d' % (x, y)) tilefile = file(os.path.join(tiledir, 'Tile.yaml')) newtile = yaml.load(tilefile) tilefile.close() if (newtile.peak[1] > peak[1]): peak = newtile.peak for treetype in newtile.trees: trees.setdefault(treetype, []).extend(newtile.trees[treetype]) if myRegion.doOre: for oretype in newtile.ores: ores.setdefault(oretype, []).extend(newtile.ores[oretype]) if args.safemerge: tileworld = mclevel.MCInfdevOldLevel(tiledir, create=False) world.copyBlocksFrom(tileworld, tileworld.bounds, tileworld.bounds.origin) tileworld = False # plant trees in our world log.log_info("Planting %d trees at the region level..." % \ sum([len(trees[treetype]) for treetype in trees])) Tree.placetreesinregion(trees, treeobjs, world) # deposit ores in our world if myRegion.doOre: log.log_info("Depositing %d ores at the region level..." % \ sum([len(ores[oretype]) for oretype in ores])) Ore.placeoreinregion(ores, oreobjs, world) # tie up loose ends world.setPlayerGameType(1) setspawnandsave(world, peak) oldyamlpath = os.path.join('Regions', args.name, 'Region.yaml') newyamlpath = os.path.join('Worlds', args.name, 'Region.yaml') shutil.copy(oldyamlpath, newyamlpath) shutil.rmtree(os.path.join('Regions', name, 'Tiles'))