diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java
index e6c66ecb4..37c73e52e 100644
--- a/src/main/java/cn/nukkit/Server.java
+++ b/src/main/java/cn/nukkit/Server.java
@@ -322,7 +322,7 @@ public Level remove(Object key) {
 
     private Watchdog watchdog;
 
-    private DB nameLookup;
+    public DB nameLookup;
 
     private PlayerDataSerializer playerDataSerializer;
 
@@ -2142,12 +2142,10 @@ public CompoundTag getOfflinePlayerData(UUID uuid, boolean create) {
         return getOfflinePlayerDataInternal(uuid.toString(), true, create);
     }
 
-    @Deprecated
     public CompoundTag getOfflinePlayerData(String name) {
         return getOfflinePlayerData(name, false);
     }
 
-    @Deprecated
     public CompoundTag getOfflinePlayerData(String name, boolean create) {
         Optional<UUID> uuid = lookupName(name);
         return getOfflinePlayerDataInternal(uuid.map(UUID::toString).orElse(name), true, create);
diff --git a/src/main/java/cn/nukkit/convert/BlockEntityConvert.java b/src/main/java/cn/nukkit/convert/BlockEntityConvert.java
new file mode 100644
index 000000000..84efb013a
--- /dev/null
+++ b/src/main/java/cn/nukkit/convert/BlockEntityConvert.java
@@ -0,0 +1,37 @@
+package cn.nukkit.convert;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+
+public class BlockEntityConvert {
+    public static void convertInventory(CompoundTag root) {
+        ListTag<CompoundTag> items = root.getList("Items", CompoundTag.class);
+        ListTag<CompoundTag> result = new ListTag<>();
+        for (var nbt : items.getAll()) {
+            int id = nbt.getShort("id");
+            int slot = nbt.getByte("Slot");
+            int count = nbt.getByte("Count");
+            int damage = nbt.getShort("Damage");
+
+            var newTag = new CompoundTag();
+            Item item = Item.get(id);
+            String namespaceId = item.getNamespaceId();
+            newTag.putByte("Count", count)
+                    .putShort("Damage", damage);
+            newTag.putString("Name", namespaceId);
+            newTag.putByte("Slot", slot);
+            if (item.hasCompoundTag()) {
+                newTag.putCompound("tag", item.getNamedTag());
+            }
+            if (item.getBlockUnsafe() != null) {
+                newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe()));
+            }
+            result.add(newTag);
+        }
+        if (result.size() != 0) {
+            root.putList("Items", result);
+        }
+    }
+}
diff --git a/src/main/java/cn/nukkit/convert/Convert.java b/src/main/java/cn/nukkit/convert/Convert.java
index 340f24b2c..24e78b519 100644
--- a/src/main/java/cn/nukkit/convert/Convert.java
+++ b/src/main/java/cn/nukkit/convert/Convert.java
@@ -6,13 +6,10 @@
 import cn.nukkit.convert.task.ConvertTask;
 import cn.nukkit.lang.PluginI18n;
 import cn.nukkit.level.DimensionData;
-import cn.nukkit.level.GameRule;
 import cn.nukkit.level.Level;
 import cn.nukkit.level.format.anvil.Anvil;
 import cn.nukkit.level.format.generic.BaseRegionLoader;
-import cn.nukkit.nbt.tag.CompoundTag;
 import cn.nukkit.network.protocol.types.GameType;
-import cn.nukkit.utils.Logger;
 import lombok.extern.slf4j.Slf4j;
 
 import java.io.File;
@@ -20,7 +17,10 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
 
 @Slf4j
 public class Convert {
@@ -29,13 +29,16 @@ public class Convert {
     public static ForkJoinPool THREAD_POOL_EXECUTOR = (ForkJoinPool) Executors.newWorkStealingPool();
 
     public static void start() {
+        PlayerDataConvert.start();
+        log.info("convert player data complete!");
+
         File file = new File("worlds", world + "/region");
         Level level = Server.getInstance().getLevelByName(world);
         Anvil levelProvider = (Anvil) level.requireProvider();
 
         try {
             DimensionData dimensionData = levelProvider.getDimensionData();
-            String path = "output/" + level.getName();
+            String path = "output/worlds/" + level.getName();
             LevelDat build = LevelDat.builder().spawnPoint(level.getSpawnLocation().asBlockVector3()).randomSeed(level.getSeed()).name(level.getName()).gameRules(level.getGameRules())
                     .gameType(GameType.SURVIVAL).build();
             LevelDBStorage levelDBStorage = new LevelDBStorage(path);
@@ -62,6 +65,7 @@ public static void start() {
                 task.get();
             }
             levelDBStorage.close();
+            log.info("All region is complete!");
         } catch (IOException | InterruptedException | ExecutionException e) {
             throw new RuntimeException(e);
         }
diff --git a/src/main/java/cn/nukkit/convert/PlayerDataConvert.java b/src/main/java/cn/nukkit/convert/PlayerDataConvert.java
new file mode 100644
index 000000000..591cc0f45
--- /dev/null
+++ b/src/main/java/cn/nukkit/convert/PlayerDataConvert.java
@@ -0,0 +1,175 @@
+package cn.nukkit.convert;
+
+import cn.nukkit.Server;
+import cn.nukkit.item.Item;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+import com.google.common.collect.HashBiMap;
+import org.iq80.leveldb.CompressionType;
+import org.iq80.leveldb.DB;
+import org.iq80.leveldb.Options;
+import org.iq80.leveldb.impl.Iq80DBFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.UUID;
+
+public class PlayerDataConvert {
+    static DB db;
+
+
+    public static void start() {
+        Server server = Server.getInstance();
+        File output = new File(server.getDataPath(), "output/players");
+        if (!output.exists()) {
+            output.mkdirs();
+        }
+        try {
+            db = Iq80DBFactory.factory.open(output, new Options()
+                    .createIfMissing(true)
+                    .compressionType(CompressionType.ZLIB_RAW));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        DB oldDB = Server.getInstance().nameLookup;
+        HashBiMap<String, UUID> name2uuid = HashBiMap.create();
+        File file = new File(server.getDataPath() + "players");
+        for (var f : Objects.requireNonNull(file.listFiles(fi -> fi.getName().endsWith(".dat")))) {
+            String sUuid = f.getName().replace(".dat", "");
+            UUID uuid = UUID.fromString(sUuid);
+            ByteBuffer buffer = ByteBuffer.allocate(16);
+            buffer.putLong(uuid.getMostSignificantBits());
+            buffer.putLong(uuid.getLeastSignificantBits());
+            byte[] v = buffer.array();
+            for (java.util.Map.Entry<byte[], byte[]> entry : oldDB) {
+                if (Arrays.equals(entry.getValue(), v)) {
+                    String s = new String(entry.getKey(), StandardCharsets.UTF_8);
+                    name2uuid.put(s, uuid);
+                    break;
+                }
+            }
+        }
+        for (var f : Objects.requireNonNull(file.listFiles(fi -> fi.getName().endsWith(".dat")))) {
+            UUID uuid = UUID.fromString(f.getName().replace(".dat", ""));
+            CompoundTag offlinePlayerData = server.getOfflinePlayerData(uuid, false);
+            convertInventory(offlinePlayerData);
+            String s = name2uuid.inverse().get(uuid);
+
+            ByteBuffer buffer = ByteBuffer.allocate(16);
+            buffer.putLong(uuid.getMostSignificantBits());
+            buffer.putLong(uuid.getLeastSignificantBits());
+            byte[] v = buffer.array();
+
+            db.put(s.getBytes(StandardCharsets.UTF_8), v);
+            try {
+                db.put(v, NBTIO.writeGZIPCompressed(offlinePlayerData, ByteOrder.BIG_ENDIAN));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        try {
+            db.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void convertInventory(CompoundTag root) {
+        ListTag<CompoundTag> inventory = root.getList("Inventory", CompoundTag.class);
+        ListTag<CompoundTag> result = new ListTag<>();
+        CompoundTag offHand = null;
+        for (var nbt : inventory.getAll()) {
+            int id = nbt.getShort("id");
+            int slot = nbt.getByte("Slot");
+            int count = nbt.getByte("Count");
+            int damage = nbt.getShort("Damage");
+
+            if (slot >= 9 && slot < 45) {
+                Item item = Item.get(id);
+                String namespaceId = item.getNamespaceId();
+                CompoundTag newTag = new CompoundTag()
+                        .putByte("Count", count)
+                        .putShort("Damage", damage);
+                newTag.putString("Name", namespaceId);
+                newTag.putByte("Slot", slot - 9);
+                if (item.hasCompoundTag()) {
+                    newTag.putCompound("tag", item.getNamedTag());
+                }
+                if (item.getBlockUnsafe() != null) {
+                    newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe()));
+                }
+                result.add(newTag);
+            } else if (slot < 104 && slot >= 100) {//armor
+                Item item = Item.get(id);
+                String namespaceId = item.getNamespaceId();
+                CompoundTag newTag = new CompoundTag()
+                        .putByte("Count", count)
+                        .putShort("Damage", damage);
+                newTag.putString("Name", namespaceId);
+                newTag.putByte("Slot", slot - 64);
+                if (item.hasCompoundTag()) {
+                    newTag.putCompound("tag", item.getNamedTag());
+                }
+                if (item.getBlockUnsafe() != null) {
+                    newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe()));
+                }
+                result.add(newTag);
+            } else if (slot == -106) {//offhand
+                offHand = new CompoundTag();
+                Item item = Item.get(id);
+                String namespaceId = item.getNamespaceId();
+                offHand.putByte("Count", count)
+                        .putShort("Damage", damage);
+                offHand.putString("Name", namespaceId);
+                offHand.putByte("Slot", 0);
+                if (item.hasCompoundTag()) {
+                    offHand.putCompound("tag", item.getNamedTag());
+                }
+                if (item.getBlockUnsafe() != null) {
+                    offHand.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe()));
+                }
+            }
+        }
+        if (offHand != null) {
+            root.putCompound("OffInventory", offHand);
+        }
+        if (result.size() != 0) {
+            root.putList("Inventory", result);
+        }
+
+        ListTag<CompoundTag> enderItems = root.getList("EnderItems", CompoundTag.class);
+        ListTag<CompoundTag> resultEnderItems = new ListTag<>();
+        for (var nbt : enderItems.getAll()) {
+            int id = nbt.getShort("id");
+            int slot = nbt.getByte("Slot");
+            int count = nbt.getByte("Count");
+            int damage = nbt.getShort("Damage");
+
+            if (slot >= 0 && slot < 27) {
+                Item item = Item.get(id);
+                String namespaceId = item.getNamespaceId();
+                CompoundTag newTag = new CompoundTag()
+                        .putByte("Count", count)
+                        .putShort("Damage", damage);
+                newTag.putString("Name", namespaceId);
+                newTag.putByte("Slot", slot);
+                if (item.hasCompoundTag()) {
+                    newTag.putCompound("tag", item.getNamedTag());
+                }
+                if (item.getBlockUnsafe() != null) {
+                    newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe()));
+                }
+                resultEnderItems.add(newTag);
+            }
+        }
+        if (resultEnderItems.size() != 0) {
+            root.putList("EnderItems", resultEnderItems);
+        }
+    }
+}
diff --git a/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java b/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java
index ae1112101..76f8190bd 100644
--- a/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java
+++ b/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java
@@ -4,6 +4,7 @@
 import cn.nukkit.blockentity.BlockEntity;
 import cn.nukkit.blockstate.BlockState;
 import cn.nukkit.blockstate.BlockStateRegistry;
+import cn.nukkit.convert.BlockEntityConvert;
 import cn.nukkit.convert.palette.Palette;
 import cn.nukkit.entity.Entity;
 import cn.nukkit.level.DimensionData;
@@ -57,6 +58,7 @@ static int index(int x, int y, int z) {
     //serialize chunk section
     private void serializeBlock(WriteBatch writeBatch, Chunk chunk, DimensionData dimensionData) {
         ChunkSection[] sections = chunk.getSections();
+        int minSubY = dimensionData.getMinHeight() >> 4;
         for (var section : sections) {
             if (section == null) {
                 continue;
@@ -66,6 +68,7 @@ private void serializeBlock(WriteBatch writeBatch, Chunk chunk, DimensionData di
                 buffer.writeByte(9);
                 buffer.writeByte(2);
                 int y = section.getY();
+                y += minSubY;
                 buffer.writeByte(y);
                 BlockStateRegistry.Registration registration = BlockStateRegistry.getRegistration(BlockState.AIR);
                 CompoundTag originalBlock = registration.originalBlock;
@@ -138,6 +141,7 @@ private void serializeTileAndEntity(WriteBatch writeBatch, Chunk chunk, Dimensio
             else {
                 for (BlockEntity blockEntity : blockEntities) {
                     blockEntity.saveNBT();
+                    BlockEntityConvert.convertInventory(blockEntity.namedTag);
                     NBTIO.write(blockEntity.namedTag, bufStream, ByteOrder.LITTLE_ENDIAN);
                 }
                 writeBatch.put(key, Utils.convertByteBuf2Array(tileBuffer));
diff --git a/src/main/java/cn/nukkit/convert/task/ConvertTask.java b/src/main/java/cn/nukkit/convert/task/ConvertTask.java
index 30d155f93..b1b4f460a 100644
--- a/src/main/java/cn/nukkit/convert/task/ConvertTask.java
+++ b/src/main/java/cn/nukkit/convert/task/ConvertTask.java
@@ -41,6 +41,7 @@ protected boolean exec() {
                     if (regionLoader.chunkExists(i, j)) {
                         try {
                             Chunk chunk = (Chunk) regionLoader.readChunk(i, j);
+                            chunk.initChunk();
                             levelDBStorage.writeChunk(chunk, dimensionData);
                         } catch (IOException e) {
                             throw new RuntimeException(e);
diff --git a/src/main/java/cn/nukkit/nbt/tag/ByteArrayTag.java b/src/main/java/cn/nukkit/nbt/tag/ByteArrayTag.java
index 93d98790b..7dca7a828 100644
--- a/src/main/java/cn/nukkit/nbt/tag/ByteArrayTag.java
+++ b/src/main/java/cn/nukkit/nbt/tag/ByteArrayTag.java
@@ -71,6 +71,9 @@ public String toSNBT() {
 
     @Override
     public String toSNBT(int space) {
+        if (this.data.length == 0) {
+            return "[B;]";
+        }
         StringBuilder builder = new StringBuilder("[B; ");
         for (int i = 0; i < this.data.length - 1; i++) {
             builder.append(data[i]).append("b, ");