def saveInPlaceGen(self): """ Save all chunks to the database, and write the root_tag back to level.dat. """ self.saving = True batch = leveldb_mcpe.WriteBatch() dirtyChunkCount = 0 for chunk in self._loadedChunks.itervalues(): if chunk.dirty: dirtyChunkCount += 1 self.worldFile.saveChunk(chunk, batch=batch) chunk.dirty = False yield with nbt.littleEndianNBT(): for p in self.players: playerData = self.playerTagCache[p] if playerData is not None: playerData = playerData.save(compressed=False) # It will get compressed in the DB itself self.worldFile.savePlayer(p, playerData, batch=batch) with self.worldFile.world_db() as db: wop = self.worldFile.writeOptions db.Write(wop, batch) self.saving = False logger.info(u"Saved {0} chunks to the database".format(dirtyChunkCount)) path = os.path.join(self.worldFile.path, 'level.dat') with nbt.littleEndianNBT(): rootTagData = self.root_tag.save(compressed=False) rootTagData = struct.Struct('<i').pack(4) + struct.Struct('<i').pack(len(rootTagData)) + rootTagData with open(path, 'w') as f: f.write(rootTagData)
def loadLevelDat(self, create=False, random_seed=None, last_played=None): """ Loads the level.dat from the worldfolder. :param create: bool. If it's True, a fresh level.dat will be created instead. :param random_seed: long :param last_played: long :return: None """ def _loadLevelDat(filename): root_tag_buf = open(filename, 'rb').read() magic, length, root_tag_buf = root_tag_buf[:4], root_tag_buf[4:8], root_tag_buf[8:] if struct.Struct('<i').unpack(magic)[0] < 3: logger.info("Found an old level.dat file. Aborting world load") raise InvalidPocketLevelDBWorldException() # Maybe try convert/load old PE world? if len(root_tag_buf) != struct.Struct('<i').unpack(length)[0]: raise nbt.NBTFormatError() self.root_tag = nbt.load(buf=root_tag_buf) if create: self._createLevelDat(random_seed, last_played) return try: with nbt.littleEndianNBT(): _loadLevelDat(os.path.join(self.worldFile.path, "level.dat")) return except (nbt.NBTFormatError, IOError) as err: logger.info("Failed to load level.dat, trying to load level.dat_old ({0})".format(err)) try: with nbt.littleEndianNBT(): _loadLevelDat(os.path.join(self.worldFile.path, "level.dat_old")) return except (nbt.NBTFormatError, IOError) as err: logger.info("Failed to load level.dat_old, creating new level.dat ({0})".format(err)) self._createLevelDat(random_seed, last_played)
def _createLevelDat(self, random_seed, last_played): """ Creates a new level.dat root_tag, and puts it in self.root_tag. To write it to the disk, self.save() should be called. :param random_seed: long :param last_played: long :return: None """ with nbt.littleEndianNBT(): root_tag = nbt.TAG_Compound() root_tag["SpawnX"] = nbt.TAG_Int(0) root_tag["SpawnY"] = nbt.TAG_Int(2) root_tag["SpawnZ"] = nbt.TAG_Int(0) if last_played is None: last_played = long(time.time() * 100) if random_seed is None: random_seed = long(numpy.random.random() * 0xffffffffffffffffL) - 0x8000000000000000L self.root_tag = root_tag self.LastPlayed = long(last_played) self.RandomSeed = long(random_seed) self.SizeOnDisk = 0 self.Time = 1 self.LevelName = os.path.basename(self.worldFile.path)
def loadNBTCompoundList(data, littleEndian=True): """ Loads a list of NBT Compound tags from a bunch of data. Uses sep to determine where the next Compound tag starts. :param data: str, the NBT to load from :param littleEndian: bool. Determines endianness :return: list of TAG_Compounds """ if type(data) is unicode: data = str(data) def load(_data): sep = "\x00\x00\x00\x00\n" sep_data = _data.split(sep) compounds = [] for d in sep_data: if len(d) != 0: if not d.startswith("\n"): d = "\n" + d tag = (nbt.load(buf=(d + '\x00\x00\x00\x00'))) compounds.append(tag) return compounds if littleEndian: with nbt.littleEndianNBT(): return load(data) else: return load(data)
def saveInPlaceGen(self): """ Save all chunks to the database, and write the root_tag back to level.dat. """ if DEBUG_PE: print "*** saveInPlaceGen" open(dump_fName, 'a').write("*** saveInPlaceGen\n") self.saving = True batch = leveldb_mcpe.WriteBatch() dirtyChunkCount = 0 for c in self.chunksNeedingLighting: self.getChunk(*c).genFastLights() for chunk in self._loadedChunks.itervalues(): if chunk.dirty: dirtyChunkCount += 1 self.worldFile.saveChunk(chunk, batch=batch) chunk.dirty = False yield with nbt.littleEndianNBT(): for p in self.players: playerData = self.playerTagCache[p] if playerData is not None: playerData = playerData.save( compressed=False ) # It will get compressed in the DB itself self.worldFile.savePlayer(p, playerData, batch=batch) with self.worldFile.world_db() as db: wop = self.worldFile.writeOptions db.Write(wop, batch) self.saving = False logger.info( u"Saved {0} chunks to the database".format(dirtyChunkCount)) path = os.path.join(self.worldFile.path, 'level.dat') with nbt.littleEndianNBT(): rootTagData = self.root_tag.save(compressed=False) rootTagData = struct.Struct('<i').pack(4) + struct.Struct( '<i').pack(len(rootTagData)) + rootTagData with open(path, 'w') as f: f.write(rootTagData)
def loadLevelDat(self, create=False, random_seed=None, last_played=None): """ Loads the level.dat from the worldfolder. :param create: bool. If it's True, a fresh level.dat will be created instead. :param random_seed: long :param last_played: long :return: None """ def _loadLevelDat(filename): root_tag_buf = open(filename, 'rb').read() magic, length, root_tag_buf = root_tag_buf[:4], root_tag_buf[ 4:8], root_tag_buf[8:] if struct.Struct('<i').unpack(magic)[0] < 3: logger.info("Found an old level.dat file. Aborting world load") raise InvalidPocketLevelDBWorldException( ) # Maybe try convert/load old PE world? if len(root_tag_buf) != struct.Struct('<i').unpack(length)[0]: raise nbt.NBTFormatError() self.root_tag = nbt.load(buf=root_tag_buf) self.__gameVersion = 'PE' id_definitions.ids_loader('PE') if create: self._createLevelDat(random_seed, last_played) return try: with nbt.littleEndianNBT(): _loadLevelDat(os.path.join(self.worldFile.path, "level.dat")) return except (nbt.NBTFormatError, IOError) as err: logger.info( "Failed to load level.dat, trying to load level.dat_old ({0})". format(err)) try: with nbt.littleEndianNBT(): _loadLevelDat( os.path.join(self.worldFile.path, "level.dat_old")) return except (nbt.NBTFormatError, IOError) as err: logger.info( "Failed to load level.dat_old, creating new level.dat ({0})". format(err)) self._createLevelDat(random_seed, last_played)
def savedData(self): """ Returns the data of the chunk to save to the database. :return: str of 83200 bytes of chunk data. """ def packData(dataArray): """ Repacks the terrain data to Mojang's leveldb library's format. """ assert dataArray.shape[2] == self.world.Height data = numpy.array(dataArray).reshape(16, 16, self.world.Height / 2, 2) data[..., 1] <<= 4 data[..., 1] |= data[..., 0] return numpy.array(data[:, :, :, 1]) if self.dirty: # elements of DirtyColumns are bitfields. Each bit corresponds to a # 16-block segment of the column. We set all of the bits because # we only track modifications at the chunk level. self.DirtyColumns[:] = 255 with nbt.littleEndianNBT(): entityData = "" tileEntityData = "" for ent in self.TileEntities: tileEntityData += ent.save(compressed=False) for ent in self.Entities: v = ent["id"].value # ent["id"] = nbt.TAG_Int(entity.PocketEntity.entityList[v]) id = entity.PocketEntity.getNumId(v) # print id if id >= 1000: print id print type(ent) print ent ent['id'] = nbt.TAG_Int(id) entityData += ent.save(compressed=False) # We have to re-invert after saving otherwise the next save will fail. ent["id"] = nbt.TAG_String(v) terrain = ''.join([ self.Blocks.tostring(), packData(self.Data).tostring(), packData(self.SkyLight).tostring(), packData(self.BlockLight).tostring(), self.DirtyColumns.tostring(), self.GrassColors.tostring(), ]) return terrain, tileEntityData, entityData
def savedData(self): """ Returns the data of the chunk to save to the database. :return: str of 83200 bytes of chunk data. """ def packData(dataArray): """ Repacks the terrain data to Mojang's leveldb library's format. """ assert dataArray.shape[2] == self.world.Height data = numpy.array(dataArray).reshape(16, 16, self.world.Height / 2, 2) data[..., 1] <<= 4 data[..., 1] |= data[..., 0] return numpy.array(data[:, :, :, 1]) if self.dirty: # elements of DirtyColumns are bitfields. Each bit corresponds to a # 16-block segment of the column. We set all of the bits because # we only track modifications at the chunk level. self.DirtyColumns[:] = 255 with nbt.littleEndianNBT(): entityData = "" tileEntityData = "" for ent in self.TileEntities: tileEntityData += ent.save(compressed=False) for ent in self.Entities: v = ent["id"].value # ent["id"] = nbt.TAG_Int(entity.PocketEntity.entityList[v]) id = entity.PocketEntity.getNumId(v) # print id if id >= 1000: print id print type(ent) print ent ent['id'] = nbt.TAG_Int(id) entityData += ent.save(compressed=False) # We have to re-invert after saving otherwise the next save will fail. ent["id"] = nbt.TAG_String(v) terrain = ''.join([self.Blocks.tostring(), packData(self.Data).tostring(), packData(self.SkyLight).tostring(), packData(self.BlockLight).tostring(), self.DirtyColumns.tostring(), self.GrassColors.tostring(), ]) return terrain, tileEntityData, entityData
def getPlayerTag(self, player='Player'): """ Obtains a player from the world. :param player: string of the name of the player. "Player" for SSP player, player_<client-id> for SMP player. :return: nbt.TAG_Compound, root tag of the player. """ if player == '[No players]': # Apparently this is being called somewhere? return None if player == 'Player': player = '~local_player' _player = self.playerTagCache.get(player) if _player is not None: return _player playerData = self.playerData[player] with nbt.littleEndianNBT(): _player = nbt.load(buf=playerData) self.playerTagCache[player] = _player return _player
if DEBUG_PE: try: open(dump_fName, 'a').write( "**********\nLongest data length: %s\nData:\n%s\n" % (longest_complist_len, longest_complist)) open(dump_fName, 'a').write( "**********\nShortest data length: %s\nData:\n%s\n" % (shortest_complist_len, shortest_complist)) except Exception, e: print "Could not write debug info:", e return compounds if littleEndian: with nbt.littleEndianNBT(): return load(data) else: return load(data) def TagProperty(tagName, tagType, default_or_func=None): """ Copied from infiniteworld.py. Custom property object to handle NBT-tag properties. :param tagName: str, Name of the NBT-tag :param tagType: int, (nbt.TAG_TYPE) Type of the NBT-tag :param default_or_func: function or default value. If function, function should return the default. :return: property """ def getter(self): if tagName not in self.root_tag: