def saveToFile(self, filename=None): """ save to file named filename, or use self.filename. XXX NOT THREAD SAFE AT ALL. """ if filename is None: filename = self.filename if filename is None: raise IOError, u"Attempted to save an unnamed schematic in place" self.Materials = self.materials.name self.root_tag["Blocks"] = nbt.TAG_Byte_Array(self._Blocks.astype('uint8')) add = self._Blocks >> 8 if add.any(): # WorldEdit AddBlocks compatibility. # The first 4-bit value is stored in the high bits of the first byte. # Increase odd size by one to align slices. packed_add = zeros(add.size + (add.size & 1), 'uint8') packed_add[:add.size] = add.ravel() # Shift even bytes to the left packed_add[::2] <<= 4 # Merge odd bytes into even bytes packed_add[::2] |= packed_add[1::2] # Save only the even bytes, now that they contain the odd bytes in their lower bits. packed_add = packed_add[0::2] self.root_tag["AddBlocks"] = nbt.TAG_Byte_Array(packed_add) with open(filename, 'wb') as chunkfh: self.root_tag.save(chunkfh) del self.root_tag["Blocks"] self.root_tag.pop("AddBlocks", None)
def createschematic(): a = (128 * 129 * (maxup + abs(maxdown) + 1)) schematic = nbt.NBTFile() schematic["Height"] = nbt.TAG_Short(value=(abs(maxdown) + maxup + 1)) schematic["Length"] = nbt.TAG_Short(value=129) schematic["Width"] = nbt.TAG_Short(value=128) tempblocks = array.array('B', [0] * a) tempdata = array.array('B', [0] * a) for x in xrange(128): for y in xrange(128): tempblocks[s(x, y)] = blockid[tempcolors[m(x, y)]] tempdata[s(x, y)] = blockdata[tempcolors[m(x, y)]] tempblocks[s(x, 128)] = 3 tempdata[s(x, 128)] = 0 schematic["Blocks"] = nbt.TAG_Byte_Array() schematic["Data"] = nbt.TAG_Byte_Array() schematic["Blocks"].value = bytearray(tempblocks) schematic["Data"].value = bytearray(tempdata) schematic["Materials"] = nbt.TAG_String(value="Alpha") print "Writing file..." schematic.write_file(schematicpath) print "Schematic successfully created!" print "max height up: " + repr(maxup) print "max height down: " + repr(abs(maxdown)) print "Minimum height you should build at: " + repr(maxup + 64)
def toNBT(self): root = nbt.TAG_Compound() root['BlockStates'] = nbt.TAG_Long_Array(self.__old_blockstates) root['Y'] = nbt.TAG_Byte(self._y) root['BlockLight'] = nbt.TAG_Byte_Array(self._block_light) root['SkyLight'] = nbt.TAG_Byte_Array(self._sky_light) palette = nbt.TAG_List() for block in self.palette: palette.append(block.toNBT()) root['Palette'] = self._palette return root
def testCreate(self): "Create an indev level." "The root of an NBT file is always a TAG_Compound." level = nbt.TAG_Compound(name="MinecraftLevel") "Subtags of a TAG_Compound are automatically named when you use the [] operator." level["About"] = nbt.TAG_Compound() level["About"]["Author"] = nbt.TAG_String("codewarrior") level["Environment"] = nbt.TAG_Compound() level["Environment"]["SkyBrightness"] = nbt.TAG_Byte(16) level["Environment"]["SurroundingWaterHeight"] = nbt.TAG_Short(32) "You can also create and name a tag before adding it to the compound." spawn = nbt.TAG_List( (nbt.TAG_Short(100), nbt.TAG_Short(45), nbt.TAG_Short(55))) spawn.name = "Spawn" mapTag = nbt.TAG_Compound() mapTag.add(spawn) mapTag.name = "Map" level.add(mapTag) "I think it looks more familiar with [] syntax." l, w, h = 128, 128, 128 mapTag["Height"] = nbt.TAG_Short(h) # y dimension mapTag["Length"] = nbt.TAG_Short(l) # z dimension mapTag["Width"] = nbt.TAG_Short(w) # x dimension "Byte arrays are stored as numpy.uint8 arrays. " mapTag["Blocks"] = nbt.TAG_Byte_Array() mapTag["Blocks"].value = numpy.zeros( l * w * h, dtype=numpy.uint8) # create lots of air! "The blocks array is indexed (y,z,x) for indev levels, so reshape the blocks" mapTag["Blocks"].value.shape = (h, l, w) "Replace the bottom layer of the indev level with wood" mapTag["Blocks"].value[0, :, :] = 5 "This is a great way to learn the power of numpy array slicing and indexing." mapTag["Data"] = nbt.TAG_Byte_Array() mapTag["Data"].value = numpy.zeros(l * w * h, dtype=numpy.uint8) return level
def save(self): root = self._nbt root['Level']['Biomes'] = nbt.TAG_Byte_Array(self._biomes) root['Level']['HeightMap'] = nbt.TAG_Int_Array( self._height_map.ravel()) tile_entities = nbt.TAG_List() for te in self._tile_entities: tile_entities.append(te) root['Level']['TileEntities'] = tile_entities entities = nbt.TAG_List() for e in self._entities: entities.append(e) root['Level']['Entities'] = entities sections = nbt.TAG_List() for section in self._sections.itervalues(): sections.append(section.toNBT()) root['Level']['Sections'] = sections self.region.saveChunk(self.cx, self.cz, root.save(compressed=False))
def __init__(self, shape=None, root_tag=None, filename=None, mats='Alpha'): """ shape is (x,y,z) for a new level's shape. if none, takes root_tag as a TAG_Compound for an existing schematic file. if none, tries to read the tag from filename. if none, results are undefined. materials can be a MCMaterials instance, or one of "Classic", "Alpha", "Pocket" to indicate allowable blocks. The default is Alpha. block coordinate order in the file is y,z,x to use the same code as classic/indev levels. in hindsight, this was a completely arbitrary decision. the Entities and TileEntities are nbt.TAG_List objects containing TAG_Compounds. this makes it easy to copy entities without knowing about their insides. rotateLeft swaps the axes of the different arrays. because of this, the Width, Height, and Length reflect the current dimensions of the schematic rather than the ones specified in the NBT structure. I'm not sure what happens when I try to re-save a rotated schematic. """ if filename: self.filename = filename if None is root_tag and os.path.exists(filename): root_tag = nbt.load(filename) else: self.filename = None if mats in namedMaterials: self.materials = namedMaterials[mats] else: assert (isinstance(mats, MCMaterials)) self.materials = mats if root_tag: self.root_tag = root_tag if "Materials" in root_tag: self.materials = namedMaterials[self.Materials] else: root_tag["Materials"] = nbt.TAG_String(self.materials.name) w = self.root_tag["Width"].value l = self.root_tag["Length"].value h = self.root_tag["Height"].value self._Blocks = self.root_tag["Blocks"].value.astype('uint16').reshape(h, l, w) # _Blocks is y, z, x del self.root_tag["Blocks"] if "AddBlocks" in self.root_tag: # Use WorldEdit's "AddBlocks" array to load and store the 4 high bits of a block ID. # Unlike Minecraft's NibbleArrays, this array stores the first block's bits in the # 4 high bits of the first byte. size = (h * l * w) # If odd, add one to the size to make sure the adjacent slices line up. add = zeros(size + (size & 1), 'uint16') # Fill the even bytes with data add[::2] = self.root_tag["AddBlocks"].value # Copy the low 4 bits to the odd bytes add[1::2] = add[::2] & 0xf # Shift the even bytes down add[::2] >>= 4 # Shift every byte up before merging it with Blocks add <<= 8 self._Blocks |= add[:size].reshape(h, l, w) del self.root_tag["AddBlocks"] self.root_tag["Data"].value = self.root_tag["Data"].value.reshape(h, l, w) if "Biomes" in self.root_tag: self.root_tag["Biomes"].value.shape = (l, w) else: assert shape is not None root_tag = nbt.TAG_Compound(name="Schematic") root_tag["Height"] = nbt.TAG_Short(shape[1]) root_tag["Length"] = nbt.TAG_Short(shape[2]) root_tag["Width"] = nbt.TAG_Short(shape[0]) root_tag["Entities"] = nbt.TAG_List() root_tag["TileEntities"] = nbt.TAG_List() root_tag["Materials"] = nbt.TAG_String(self.materials.name) self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16') root_tag["Data"] = nbt.TAG_Byte_Array(zeros((shape[1], shape[2], shape[0]), uint8)) root_tag["Biomes"] = nbt.TAG_Byte_Array(zeros((shape[2], shape[0]), uint8)) self.root_tag = root_tag self.root_tag["Data"].value &= 0xF # discard high bits
def saveToFile(self, filename=None): if filename is None: filename = self.filename if filename is None: log.warn(u"Attempted to save an unnamed file in place") return # you fool! self.Data <<= 4 self.Data |= (self.BlockLight & 0xf) self.Blocks = swapaxes(self.Blocks, 0, 2) self.Data = swapaxes(self.Data, 0, 2) mapTag = nbt.TAG_Compound() mapTag["Width"] = nbt.TAG_Short(self.Width) mapTag["Height"] = nbt.TAG_Short(self.Height) mapTag["Length"] = nbt.TAG_Short(self.Length) mapTag["Blocks"] = nbt.TAG_Byte_Array(self.Blocks) mapTag["Data"] = nbt.TAG_Byte_Array(self.Data) self.Blocks = swapaxes(self.Blocks, 0, 2) self.Data = swapaxes(self.Data, 0, 2) mapTag[Spawn] = nbt.TAG_List([nbt.TAG_Short(i) for i in self.Spawn]) self.root_tag["Map"] = mapTag self.Entities.append(self.LocalPlayer) # fix up Entities imported from Alpha worlds def numbersToFloats(ent): for attr in "Motion", "Pos": if attr in ent: ent[attr] = nbt.TAG_List([nbt.TAG_Double(t.value) for t in ent[attr]]) for ent in self.Entities: numbersToFloats(ent) # fix up TileEntities imported from Alpha worlds. for ent in self.TileEntities: if "Pos" not in ent and all(c in ent for c in 'xyz'): ent["Pos"] = nbt.TAG_Int(self.encodePos(ent['x'].value, ent['y'].value, ent['z'].value)) # output_file = gzip.open(self.filename, "wb", compresslevel=1) try: os.rename(filename, filename + ".old") except Exception: pass try: self.root_tag.save(filename) except: os.rename(filename + ".old", filename) try: os.remove(filename + ".old") except Exception: pass self.Entities.remove(self.LocalPlayer) self.BlockLight = self.Data & 0xf self.Data >>= 4
def __init__(self, shape=None, root_tag=None, filename=None, mats='Alpha'): """ shape is (x,y,z) for a new level's shape. if none, takes root_tag as a TAG_Compound for an existing schematic file. if none, tries to read the tag from filename. if none, results are undefined. materials can be a MCMaterials instance, or one of "Classic", "Alpha", "Pocket" to indicate allowable blocks. The default is Alpha. block coordinate order in the file is y,z,x to use the same code as classic/indev levels. in hindsight, this was a completely arbitrary decision. the Entities and TileEntities are nbt.TAG_List objects containing TAG_Compounds. this makes it easy to copy entities without knowing about their insides. rotateLeft swaps the axes of the different arrays. because of this, the Width, Height, and Length reflect the current dimensions of the schematic rather than the ones specified in the NBT structure. I'm not sure what happens when I try to re-save a rotated schematic. """ # if(shape != None): # self.setShape(shape) if filename: self.filename = filename if None is root_tag and os.path.exists(filename): root_tag = nbt.load(filename) else: self.filename = None if mats in namedMaterials: self.materials = namedMaterials[mats] else: assert (isinstance(mats, MCMaterials)) self.materials = mats if root_tag: self.root_tag = root_tag if "Materials" in root_tag: self.materials = namedMaterials[self.Materials] else: root_tag["Materials"] = nbt.TAG_String(self.materials.name) self.shapeChunkData() else: assert shape is not None root_tag = nbt.TAG_Compound(name="Schematic") root_tag["Height"] = nbt.TAG_Short(shape[1]) root_tag["Length"] = nbt.TAG_Short(shape[2]) root_tag["Width"] = nbt.TAG_Short(shape[0]) root_tag["Entities"] = nbt.TAG_List() root_tag["TileEntities"] = nbt.TAG_List() root_tag["Materials"] = nbt.TAG_String(self.materials.name) root_tag["Blocks"] = nbt.TAG_Byte_Array( zeros((shape[1], shape[2], shape[0]), uint8)) root_tag["Data"] = nbt.TAG_Byte_Array( zeros((shape[1], shape[2], shape[0]), uint8)) self.root_tag = root_tag self.packUnpack() self.root_tag["Data"].value &= 0xF # discard high bits
def testCreate(self): "Create an indev level." # The root of an NBT file is always a TAG_Compound. level = nbt.TAG_Compound(name="MinecraftLevel") # Subtags of a TAG_Compound are automatically named when you use the [] operator. level["About"] = nbt.TAG_Compound() level["About"]["Author"] = nbt.TAG_String("codewarrior") level["About"]["CreatedOn"] = nbt.TAG_Long(time.time()) level["Environment"] = nbt.TAG_Compound() level["Environment"]["SkyBrightness"] = nbt.TAG_Byte(16) level["Environment"]["SurroundingWaterHeight"] = nbt.TAG_Short(32) level["Environment"]["FogColor"] = nbt.TAG_Int(0xcccccc) entity = nbt.TAG_Compound() entity["id"] = nbt.TAG_String("Creeper") entity["Pos"] = nbt.TAG_List( [nbt.TAG_Float(d) for d in (32.5, 64.0, 33.3)]) level["Entities"] = nbt.TAG_List([entity]) # You can also create and name a tag before adding it to the compound. spawn = nbt.TAG_List( (nbt.TAG_Short(100), nbt.TAG_Short(45), nbt.TAG_Short(55))) spawn.name = "Spawn" mapTag = nbt.TAG_Compound() mapTag.add(spawn) mapTag.name = "Map" level.add(mapTag) mapTag2 = nbt.TAG_Compound([spawn]) mapTag2.name = "Map" # I think it looks more familiar with [] syntax. l, w, h = 128, 128, 128 mapTag["Height"] = nbt.TAG_Short(h) # y dimension mapTag["Length"] = nbt.TAG_Short(l) # z dimension mapTag["Width"] = nbt.TAG_Short(w) # x dimension # Byte arrays are stored as numpy.uint8 arrays. mapTag["Blocks"] = nbt.TAG_Byte_Array() mapTag["Blocks"].value = numpy.zeros( l * w * h, dtype=numpy.uint8) # create lots of air! # The blocks array is indexed (y,z,x) for indev levels, so reshape the blocks mapTag["Blocks"].value.shape = (h, l, w) # Replace the bottom layer of the indev level with wood mapTag["Blocks"].value[0, :, :] = 5 # This is a great way to learn the power of numpy array slicing and indexing. mapTag["Data"] = nbt.TAG_Byte_Array() mapTag["Data"].value = numpy.zeros(l * w * h, dtype=numpy.uint8) # Save a few more tag types for completeness level["ShortArray"] = nbt.TAG_Short_Array( numpy.zeros((16, 16), dtype='uint16')) level["IntArray"] = nbt.TAG_Int_Array( numpy.zeros((16, 16), dtype='uint32')) level["Float"] = nbt.TAG_Float(0.3) return level