def testINVEditChests(self): info("INVEdit chest") invFile = mclevel.fromFile("schematics/Chests/TinkerersBox.inv") info("Blocks: %s", invFile.Blocks) info("Data: %s", invFile.Data) info("Entities: %s", invFile.Entities) info("TileEntities: %s", invFile.TileEntities)
def _import(self, command): """ import <filename> <destPoint> [noair] [nowater] Imports a level or schematic into this world, beginning at destPoint. Supported formats include - Classic single-player .mine, - Classic multiplayer server_level.dat, - Indev .mclevel - Schematic from RedstoneSim, MCEdit, mce - .inv from INVEdit (appears as a chest) """ if len(command) == 0: self.printUsage("import") return; filename = command.pop(0) destPoint = self.readPoint(command) blocksToCopy = self.readBlocksToCopy(command) importLevel = mclevel.fromFile(filename) destBox = BoundingBox(destPoint, importLevel.size) self.level.createChunksInBox(destBox); self.level.copyBlocksFrom(importLevel, importLevel.getWorldBounds(), destPoint, blocksToCopy); self.needsSave = True; print "Imported {0} blocks.".format(importLevel.getWorldBounds().volume)
def loadWorld(self, world): worldpath = os.path.expanduser(world) if os.path.exists(worldpath): self.level = mclevel.fromFile(worldpath) else: self.level = mclevel.loadWorld(world)
def _import(self, command): """ import <filename> <destPoint> [noair] [nowater] Imports a level or schematic into this world, beginning at destPoint. Supported formats include - Alpha single or multiplayer world folder containing level.dat, - Zipfile containing Alpha world folder, - Classic single-player .mine, - Classic multiplayer server_level.dat, - Indev .mclevel - Schematic from RedstoneSim, MCEdit, mce - .inv from INVEdit (appears as a chest) """ if len(command) == 0: self.printUsage("import") return filename = command.pop(0) destPoint = self.readPoint(command) blocksToCopy = self.readBlocksToCopy(command) importLevel = mclevel.fromFile(filename) self.level.copyBlocksFrom(importLevel, importLevel.bounds, destPoint, blocksToCopy, create=True) self.needsSave = True print "Imported {0} blocks.".format(importLevel.bounds.volume)
def testCreate(self): # log.info("Schematic from indev") size = (64, 64, 64) temp = mktemp("testcreate.schematic") schematic = MCSchematic(shape=size, filename=temp, mats='Classic') level = self.indevLevel.level schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), (64, 64, 64,)), (0, 0, 0)) assert((schematic.Blocks[0:64, 0:64, 0:64] == level.Blocks[0:64, 0:64, 0:64]).all()) schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), (64, 64, 64,)), (-32, -32, -32)) assert((schematic.Blocks[0:32, 0:32, 0:32] == level.Blocks[32:64, 32:64, 32:64]).all()) schematic.saveInPlace() schem = mclevel.fromFile("schematics/CreativeInABox.schematic") tempSchematic = MCSchematic(shape=(1, 1, 3)) tempSchematic.copyBlocksFrom(schem, BoundingBox((0, 0, 0), (1, 1, 3)), (0, 0, 0)) level = self.anvilLevel.level for cx, cz in itertools.product(xrange(0, 4), xrange(0, 4)): try: level.createChunk(cx, cz) except ValueError: pass schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), (64, 64, 64,)), (0, 0, 0)) schematic.close() os.remove(temp)
def find_edges(worldDir, edgeFilename): level = mclevel.fromFile(worldDir) edgeFile = open(edgeFilename, "w") sys.stdout.write("finding edges...") chunks = [] for chunk in level.allChunks: chunks.append(chunk) erodeTasks = [] examined = 0 lastProgress = 0 numChunks = len(chunks) for chunk in chunks: checkChunk(level, chunk, erodeTasks) examined += 1 progress = examined * 100 / numChunks if progress != lastProgress: lastProgress = progress sys.stdout.write("\rfinding edges (%d%%)..." % (progress)) print("") edgeFile.write("# erodeType erodeDirection posX posZ\n") numEdgeChunks = 0 for task in erodeTasks: edgeFile.write("%s\n" % (task)) numEdgeChunks += 1 edgeFile.close() print("found %d edge(s)" % (numEdgeChunks))
def loadWorld(self, world): try: worldNum = int(world) if str(worldNum) == world: self.level = mclevel.loadWorldNumber(worldNum) except ValueError: self.level = mclevel.fromFile(world)
def testCreate(self): # info("Schematic from indev") size = (64, 64, 64) temp = mktemp("testcreate.schematic") schematic = MCSchematic(shape=size, filename=temp, mats='Classic') level = self.indevlevel.level self.failUnlessRaises( ValueError, lambda: (schematic.copyBlocksFrom( level, BoundingBox((-32, -32, -32), ( 64, 64, 64, )), (0, 0, 0)))) schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), ( 64, 64, 64, )), (0, 0, 0)) assert ((schematic.Blocks[0:64, 0:64, 0:64] == level.Blocks[0:64, 0:64, 0:64]).all()) schematic.compress() schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), ( 64, 64, 64, )), (-32, -32, -32)) assert ((schematic.Blocks[0:32, 0:32, 0:32] == level.Blocks[32:64, 32:64, 32:64]).all()) schematic.compress() schematic.saveInPlace() schem = mclevel.fromFile("schematics/CreativeInABox.schematic") tempSchematic = MCSchematic(shape=(1, 1, 3)) tempSchematic.copyBlocksFrom(schem, BoundingBox((0, 0, 0), (1, 1, 3)), (0, 0, 0)) info("Schematic from alpha") level = self.alphalevel.level for cx, cz in itertools.product(xrange(0, 4), xrange(0, 4)): try: level.createChunk(cx, cz) except ValueError: pass schematic.copyBlocksFrom(level, BoundingBox((0, 0, 0), ( 64, 64, 64, )), (0, 0, 0)) schematic.close() os.remove(temp)
def testImportSchematic(self): level = self.anvilLevel.level cx, cz = level.allChunks.next() schem = mclevel.fromFile("schematics/CreativeInABox.schematic") box = BoundingBox((cx * 16, 64, cz * 16), schem.bounds.size) level.copyBlocksFrom(schem, schem.bounds, (0, 64, 0)) schem = MCSchematic(shape=schem.bounds.size) schem.copyBlocksFrom(level, box, (0, 0, 0)) convertedSourceBlocks, convertedSourceData = block_copy.convertBlocks(schem, level, schem.Blocks, schem.Data) assert (level.getChunk(cx, cz).Blocks[0:1, 0:3, 64:65] == convertedSourceBlocks).all()
def loadWorld(self, world, dimension): worldpath = os.path.expanduser(world) if os.path.exists(worldpath): level = mclevel.fromFile(worldpath) else: level = mclevel.loadWorld(world) if dimension is not None: if dimension in level.dimensions: level = level.dimensions[dimension] else: raise InvalidDimensionError, "Dimension {0} does not exist".format(dimension) return level
def __init__(self, filename, createFunc=None): if not os.path.exists(filename): filename = join("testfiles", filename) tmpname = mktemp(os.path.basename(filename)) if os.path.exists(filename): if os.path.isdir(filename): shutil.copytree(filename, tmpname) else: shutil.copy(filename, tmpname) else: createFunc(tmpname) self.tmpname = tmpname self.level = mclevel.fromFile(tmpname)
def testImportSchematic(self): level = self.alphalevel.level cx, cz = level.allChunks.next() schem = mclevel.fromFile("schematics/CreativeInABox.schematic") box = BoundingBox((cx * 16, 64, cz * 16), schem.bounds.size) level.copyBlocksFrom(schem, schem.bounds, (0, 64, 0)) schem = MCSchematic(shape=schem.bounds.size) schem.copyBlocksFrom(level, box, (0, 0, 0)) convertedSourceBlocks, convertedSourceData = schem.convertBlocksFromLevel( level, schem.Blocks, schem.Data) assert (level.getChunk( cx, cz).Blocks[0:1, 0:3, 64:65] == convertedSourceBlocks).all()
def manmade_relight(): t = templevel.TempLevel("TimeRelight", createFunc=lambda f:MCInfdevOldLevel(f, create=True)) world = t.level station = mclevel.fromFile("testfiles/station.schematic") times = 2 for x in range(times): for z in range(times): world.copyBlocksFrom(station, station.bounds, (x * station.Width, 63, z * station.Length), create=True) t = timeit(lambda: world.generateLights(world.allChunks), number=1) print "Relight manmade building: %d chunks in %.02f seconds (%.02fms per chunk)" % (world.chunkCount, t, t / world.chunkCount * 1000)
def extractZipSchematicFromIter(sourceLevel, box, zipfilename=None, entities=True): # converts classic blocks to alpha # probably should only apply to alpha levels if zipfilename is None: zipfilename = tempfile.mktemp("zipschematic") p = sourceLevel.adjustExtractionParameters(box) if p is None: return sourceBox, destPoint = p destPoint = (0, 0, 0) tempfolder = tempfile.mktemp("schematic") try: tempSchematic = MCInfdevOldLevel(tempfolder, create=True) tempSchematic.materials = sourceLevel.materials for i in tempSchematic.copyBlocksFromIter(sourceLevel, sourceBox, destPoint, entities=entities, create=True): yield i tempSchematic.saveInPlace( ) # lights not needed for this format - crashes minecraft though schematicDat = nbt.TAG_Compound() schematicDat.name = "Mega Schematic" schematicDat["Width"] = nbt.TAG_Int(sourceBox.size[0]) schematicDat["Height"] = nbt.TAG_Int(sourceBox.size[1]) schematicDat["Length"] = nbt.TAG_Int(sourceBox.size[2]) schematicDat["Materials"] = nbt.TAG_String( tempSchematic.materials.name) schematicDat.save(os.path.join(tempfolder, "schematic.dat")) zipdir(tempfolder, zipfilename) import mclevel yield mclevel.fromFile(zipfilename) finally: # We get here if the generator is GCed also if os.path.exists(tempfolder): shutil.rmtree(tempfolder, False)
def loadWorld(self, world): try: worldNum = int(world) except ValueError: self.level = mclevel.fromFile(world) self.filename = self.level.filename else: if str(worldNum) == world: if worldNum > 0 and worldNum <= 5: self.level = mclevel.loadWorldNumber(worldNum) self.filename = self.level.filename
def __init__(self, filename, createFunc=None): if not os.path.exists(filename): filename = join("testfiles", filename) tmpname = mktemp(os.path.basename(filename)) if os.path.exists(filename): if os.path.isdir(filename): shutil.copytree(filename, tmpname) else: shutil.copy(filename, tmpname) elif createFunc: createFunc(tmpname) else: raise IOError, "File %s not found." % filename self.tmpname = tmpname self.level = mclevel.fromFile(tmpname) atexit.register(self.removeTemp)
def loadWorld(self, world): try: worldNum = int(world) except ValueError: self.level = mclevel.fromFile(world) self.filename = self.level.filename self.shortWorld = os.path.split(self.level.filename)[1]; if self.shortWorld == "level.dat": self.shortWorld = os.path.split(os.path.split(self.level.filename)[0])[1]; else: if str(worldNum) == world: if worldNum > 0 and worldNum <= 5: self.level = mclevel.loadWorldNumber(worldNum) self.filename = self.level.filename self.shortWorld = "World{0}".format(worldNum)
def extractZipSchematicFromIter(sourceLevel, box, zipfilename=None, entities=True): # converts classic blocks to alpha # probably should only apply to alpha levels if zipfilename is None: zipfilename = tempfile.mktemp("zipschematic") p = sourceLevel.adjustExtractionParameters(box) if p is None: return sourceBox, destPoint = p destPoint = (0, 0, 0) tempfolder = tempfile.mktemp("schematic") try: tempSchematic = MCInfdevOldLevel(tempfolder, create=True) tempSchematic.materials = sourceLevel.materials for i in tempSchematic.copyBlocksFromIter(sourceLevel, sourceBox, destPoint, entities=entities, create=True): yield i tempSchematic.saveInPlace() # lights not needed for this format - crashes minecraft though schematicDat = nbt.TAG_Compound() schematicDat.name = "Mega Schematic" schematicDat["Width"] = nbt.TAG_Int(sourceBox.size[0]) schematicDat["Height"] = nbt.TAG_Int(sourceBox.size[1]) schematicDat["Length"] = nbt.TAG_Int(sourceBox.size[2]) schematicDat["Materials"] = nbt.TAG_String(tempSchematic.materials.name) schematicDat.save(os.path.join(tempfolder, "schematic.dat")) zipdir(tempfolder, zipfilename) import mclevel yield mclevel.fromFile(zipfilename) finally: # We get here if the generator is GCed also if os.path.exists(tempfolder): shutil.rmtree(tempfolder, False)
def main(): worldLocation = "/Users/leifgehrmann/Library/Application Support/minecraft/saves/" worldName = "OverworldTest" world = mclevel.fromFile(worldLocation + worldName) chunkPositions = list(world.allChunks) numberOfChunks = len(chunkPositions) numberOfChunks = 1 for i in range(0, numberOfChunks): xPos, zPos = chunkPositions[6] chunk = world.getChunk(xPos, zPos) c = Column(0) c.addBlockAndData(35, 1) c.addBlockAndData(35, 4) c.addBlock(17) for x in range(0, 15): for z in range(0, 15): replaceColumn(chunk, x, z, c) # chunk.Blocks[:,:,64:] = 0; chunk.chunkChanged() # world.generateLights(); world.saveInPlace() if len(sys.argv) > 1: Hello(sys.argv[1])
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 _reload(self, command): self.level = mclevel.fromFile(self.filename);
def smooth(worldDir, edgeFilename, width = 16): level = mclevel.fromFile(worldDir) newEdgeFile = open(edgeFilename + ".tmp", "w") edgeFile = open(edgeFilename, "r") width = int(width) / 2 erosionTasks = [] for line in edgeFile.readlines(): originalLine = line line = line.strip() # Preserve comments if line.startswith("#"): newEdgeFile.write(originalLine) else: task = ErosionTask.fromString(line) erosionTasks.append(task) edgeFile.close() numTasks = len(erosionTasks) skipped = 0 smoothed = 0 treeDecayList = [] if erosionTasks: examined = 0 for erosionTask in erosionTasks: examined += 1 sys.stdout.write("\rexamining edge %d of %d..." % (examined, numTasks)) # If the task didn't run (because it requires chunks that # haven't been generated yet), write it back to edges.txt. if erosionTask.run(level, treeDecayList, width): smoothed += 1 else: skipped += 1 newEdgeFile.write("%s\n" % (task)) print("") print("decaying %d pieces of eroded trees..." % (len(treeDecayList))) decay_trees(level, treeDecayList) print("saving changes...") level.saveInPlace() newEdgeFile.close() if smoothed: print("smoothed %d edge(s)" % (smoothed)) shutil.move(newEdgeFile.name, edgeFilename) else: os.remove(newEdgeFile.name) if skipped: print("%d edge(s) can't be smoothed yet, since they're not fully explored" % (skipped)) elif smoothed == numTasks: print("the map is perfectly smoothed -- nothing to do!")
def make_pdf(user_id, x1, z1, y1, x2, z2, y2): x_min = min(int(x1), int(x2)) x_max = max(int(x1), int(x2)) y_min = min(int(y1), int(y2)) y_max = max(int(y1), int(y2)) z_min = min(int(z1), int(z2)) z_max = max(int(z1), int(z2)) x_diff = x_max - x_min y_diff = y_max - y_min z_diff = z_max - z_min level = mclevel.fromFile( "/root/pymclevel/data/663/map" ) #"/root/pymclevel/data/" + str(user_id) + "/map") map = np.zeros((x_diff, y_diff, z_diff)) types = [] def show(map): print(map.shape) for z in range(map.shape[2]): for x in range(map.shape[0]): for y in range(map.shape[1]): print(int(map[x, y, z])), print print for z in range(z_min, z_max): for x in range(x_min, x_max): for y in range(y_min, y_max): chunk = level.getChunk(x / 16, y / 16) block = int(chunk.Blocks[x % 16, y % 16, z]) map[x - x_min, y - y_min, z - z_min] = block types_temp = [] colors = [(0, 0, 0), (129, 207, 224), (255, 0, 0), (0, 255, 0), (0, 0, 255), (0, 255, 255), (255, 0, 255), (255, 255, 0)] for x in range(x_diff): for y in range(y_diff): for z in range(z_diff): block = map[x, y, z] if block not in types_temp: types_temp.append(int(block)) types_temp = sorted(types_temp) for x in range(x_diff): for y in range(y_diff): for z in range(z_diff): block = map[x, y, z] if block != 0: map[x, y, z] = 1 for x in range(x_diff): for y in range(y_diff): for z in range(z_diff): block = map[x, y, z] if block not in types: types.append(int(block)) types = sorted(types) pixel_size = 10 for x in range(x_diff): for y in range(y_diff): for z in range(z_diff): block = map[x, y, z] if block != 0: map[x, y, z] = 1 def draw_level(pixel_size, map): image = np.zeros(((map.shape[0] + 1) * pixel_size, (map.shape[1] + 1) * pixel_size, 3), dtype=np.uint8) for x in range(map.shape[0]): for y in range(map.shape[1]): image[x * pixel_size:(x + 1) * pixel_size, y * pixel_size:(y + 1) * pixel_size] = colors[int(map[x, y])] return image class Tile: def __init__(self, type): self.seen = False self.type = type def spread(flat, type, x, y): if x < 0 or x >= len(flat[0]) or y < 0 or y >= len(flat): return [] if flat[y][x].seen or flat[y][x].type != type: return [] flat[y][x].seen = True visited = [] visited += spread(flat, type, x + 1, y) visited += spread(flat, type, x, y + 1) visited += spread(flat, type, x - 1, y) visited += spread(flat, type, x, y - 1) visited.append((x, y)) return visited def get_cutouts(type): cutouts = [] for z in range(z_diff): flat = [[Tile(map[x, y, z]) for x in range(x_diff)] for y in range(y_diff)] for x in range(x_diff): for y in range(y_diff): cutout = spread(flat, type, x, y) if cutout != []: cutouts.append(cutout) return cutouts def normalize(cutouts): normalized = [] for cutout in cutouts: min_x = min([a[0] for a in cutout]) min_y = min([a[1] for a in cutout]) normalized.append([(a[0] - min_x, a[1] - min_y) for a in cutout]) return normalized map_x = 100 map_y = 140 for type in types: if type != 0: cutouts = normalize(get_cutouts(type)) cutouts = sorted( cutouts, key=lambda cut: max([a[1] for a in cut])) # Sort by cutout height layers = [[cutouts[0]]] for cutout in cutouts[1:]: prev_width = max([ max([block[0] for block in peice]) for peice in layers[-1] ]) + 1 if max([a[0] for a in cutout]) + prev_width < map_x: layers[-1].append([(block[0] + prev_width, block[1]) for block in cutout]) else: layers.append([cutout]) """ first = layers[0::2] # Every second, largest first second = layers[1::2][::-1] # Every second, smallest first, oriented backwards interleaved = [] for a in range(len(first)): interleaved.append(first[a]) if a < len(second): interleaved.append(second[a]) """ layout = [] prev_height = 0 for layer in layers: print(prev_height) prev_height += max( [max([block[1] for block in peice]) for peice in layer]) + 1 for peice in layer: layout.append([(block[0], block[1] + prev_height) for block in peice]) max_x = max( [max([block[0] for block in peice]) for peice in layout]) max_y = max( [max([block[1] for block in peice]) for peice in layout]) print(max_x, max_y) sheet = np.zeros((max_x + 1, max_y + 1)) for peice in layout: for block in peice: sheet[block[0], block[1]] = 1 outlines = [] for peice in layout: outline = [] for block in peice: line0 = ((block[0], block[1]), (block[0], block[1] + 1)) line1 = ((block[0], block[1]), (block[0] + 1, block[1])) line2 = ((block[0] + 1, block[1]), (block[0] + 1, block[1] + 1)) line3 = ((block[0], block[1] + 1), (block[0] + 1, block[1] + 1)) if line0 in outline: outline.remove(line0) else: outline.append(line0) if line1 in outline: outline.remove(line1) else: outline.append(line1) if line2 in outline: outline.remove(line2) else: outline.append(line2) if line3 in outline: outline.remove(line3) else: outline.append(line3) for line in outline: if line not in outlines: outlines.append(line) outline_text = "" for line in outlines: outline_text += "line " + str(line[0][0]) + "," + str( line[0][1]) + " " + str(line[1][0]) + "," + str( line[1][1]) + "\n\n" write_to = open( "/root/pymclevel/data/" + str(user_id) + "/commands.scr", "w") write_to.write(outline_text) write_to.close() os.mkdir("/root/pymclevel/data/" + str(user_id) + "/layout") image_names = [] for z in range(map.shape[2]): data = draw_level(10, map[:, :, z]) img = Image.fromarray(data, 'RGB') img_name = "/root/pymclevel/data/" + str(user_id) + "/layout/" + str( z) + ".png" image_names.append(img_name) img.save(img_name) pdf = FPDF() pdf.add_page() pdf.image("/root/pymclevel/front_page.jpg", 0, 0, 240, 240) # imagelist is the list with all image filenames for image in image_names: pdf.add_page() pdf.image(image, 30, 30, 150, 150) pdf.output("/root/pymclevel/data/" + str(user_id) + "/layout.pdf", "F")
import sys sys.path.append('/Users/nanne/Desktop/pymclevel') import mclevel from os import listdir from os.path import isfile, join onlyfiles = [f for f in listdir(".") if isfile(join(".", f)) and f.count('_') == 1] for f in onlyfiles: scheme = mclevel.fromFile(f) scheme.saveToFile(f.replace(".", "_0.")) scheme.rotateLeft() scheme.saveToFile(f.replace(".", "_1.")) scheme.rotateLeft() scheme.saveToFile(f.replace(".", "_2.")) scheme.rotateLeft() scheme.saveToFile(f.replace(".", "_3."))
def natural_relight(): world = mclevel.fromFile("testfiles/AnvilWorld") t = timeit(lambda: world.generateLights(world.allChunks), number=1) print "Relight natural terrain: %d chunks in %.02f seconds (%.02fms per chunk)" % (world.chunkCount, t, t / world.chunkCount * 1000)
def bits(num): acc = 0 while num: acc += 1 num = num & (num - 1) return acc for x in dedup_table: # Compare rightside-up and upside-down versions dedup_table[x] = min(dedup((x&0x00F)<<8 | (x&0x0F0) | (x&0xF00)>>8), dedup(x&0xFFF)) | x & 0x1000 if bits(x) != bits(dedup_table[x]): raise ValueError("Whoops!") if __name__ == '__main__': world = mclevel.fromFile("../World2.bigger") locs = get_locs_for_world(world) cPickle.dump(locs, file("cached_locs", "wb"), 2) if __name__ == '__main__': locs = cPickle.load(file("cached_locs", "rb")) print "Found %d diamond ore" % len(locs) clusters = [] while len(locs): clusters.append(remove_cluster(locs)) cluster_sizes = [0]*20 by_slice = [0]*20 maxes = [[0]*20 for x in range(3)] cluster_shapes = {}
def testINVEditChests(self): invFile = mclevel.fromFile("schematics/Chests/TinkerersBox.inv") assert invFile.Blocks.any() assert not invFile.Data.any() assert len(invFile.Entities) == 0 assert len(invFile.TileEntities) == 1
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 setUp(self): self.test_level = mclevel.fromFile("tests/test_map/testworld/level.dat")
import mclevel # https://github.com/mcedit/pymclevel.git import PIL.Image level = mclevel.fromFile("level.dat") s = set() # 3, 45, 99 # search for height # slow!!! QQ for h in range(128): for a in range(512): for b in range(512): if level.blockAt(a, h, b) == 49: s.add(h) def dump_at_height(h, fn): img = PIL.Image.new('RGB', (512,512)) for x in range(512): for y in range(512): if level.blockAt(x, 3, y) != 49: img.putpixel((x, y), (255, 255,255)) img.save(open(fn, 'wb'), 'PNG') for i, h in enumerate(s): dump_at_height(h, '%d.png' % (i+1))
def _reload(self, command): self.level = mclevel.fromFile(self.level.filename)