diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java index 2fed9a01f..a99a0cbd4 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java @@ -71,7 +71,7 @@ public ChestInventory getRealInventory() { return (ChestInventory) inventory; } - protected void checkPairing() { + public void checkPairing() { BlockEntityChest pair = this.getPair(); if (pair != null) { diff --git a/src/main/java/cn/nukkit/convert/BlockEntityConvert.java b/src/main/java/cn/nukkit/convert/BlockEntityConvert.java index 84efb013a..2fb3dbc6d 100644 --- a/src/main/java/cn/nukkit/convert/BlockEntityConvert.java +++ b/src/main/java/cn/nukkit/convert/BlockEntityConvert.java @@ -6,7 +6,7 @@ import cn.nukkit.nbt.tag.ListTag; public class BlockEntityConvert { - public static void convertInventory(CompoundTag root) { + public static void convertBlockEntity(CompoundTag root) { ListTag items = root.getList("Items", CompoundTag.class); ListTag result = new ListTag<>(); for (var nbt : items.getAll()) { @@ -14,6 +14,7 @@ public static void convertInventory(CompoundTag root) { int slot = nbt.getByte("Slot"); int count = nbt.getByte("Count"); int damage = nbt.getShort("Damage"); + CompoundTag tag = nbt.contains("tag") ? nbt.getCompound("tag") : null; var newTag = new CompoundTag(); Item item = Item.get(id); @@ -22,8 +23,8 @@ public static void convertInventory(CompoundTag root) { .putShort("Damage", damage); newTag.putString("Name", namespaceId); newTag.putByte("Slot", slot); - if (item.hasCompoundTag()) { - newTag.putCompound("tag", item.getNamedTag()); + if (tag != null) { + newTag.putCompound("tag", tag); } if (item.getBlockUnsafe() != null) { newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe())); diff --git a/src/main/java/cn/nukkit/convert/PlayerDataConvert.java b/src/main/java/cn/nukkit/convert/PlayerDataConvert.java index 591cc0f45..13661819f 100644 --- a/src/main/java/cn/nukkit/convert/PlayerDataConvert.java +++ b/src/main/java/cn/nukkit/convert/PlayerDataConvert.java @@ -5,7 +5,7 @@ import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.ListTag; -import com.google.common.collect.HashBiMap; +import me.tongfei.progressbar.ProgressBar; import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.DB; import org.iq80.leveldb.Options; @@ -16,9 +16,9 @@ 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; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinTask; public class PlayerDataConvert { static DB db; @@ -38,39 +38,81 @@ public static void start() { throw new RuntimeException(e); } DB oldDB = Server.getInstance().nameLookup; - HashBiMap name2uuid = HashBiMap.create(); + ConcurrentHashMap name2uuid = new ConcurrentHashMap<>(); 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 entry : oldDB) { - if (Arrays.equals(entry.getValue(), v)) { - String s = new String(entry.getKey(), StandardCharsets.UTF_8); - name2uuid.put(s, uuid); - break; - } + File[] files = Objects.requireNonNull(file.listFiles(fi -> fi.getName().endsWith(".dat"))); + List task = new ArrayList<>(); + List> taskList = new ArrayList<>(); + for (File value : files) { + task.add(value); + if (task.size() > 100) { + final var t = new ArrayList<>(task); + task.clear(); + taskList.add(Server.getInstance().computeThreadPool.submit(() -> { + for (var f : t) { + 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 (Map.Entry entry : oldDB) { + if (Arrays.equals(entry.getValue(), v)) { + String s = new String(entry.getKey(), StandardCharsets.UTF_8); + name2uuid.put(uuid, s); + 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(); + if (!task.isEmpty()) { + taskList.add(Server.getInstance().computeThreadPool.submit(() -> { + for (var f : task) { + 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 entry : oldDB) { + if (Arrays.equals(entry.getValue(), v)) { + String s = new String(entry.getKey(), StandardCharsets.UTF_8); + name2uuid.put(uuid, s); + break; + } + } + } + })); + } + for (var t : taskList) { + t.join(); + } + System.out.println(name2uuid.size()); + System.out.println(files.length); + try (ProgressBar pb = new ProgressBar("Convert Player", files.length)) { + for (var f : files) { + pb.step(); + UUID uuid = UUID.fromString(f.getName().replace(".dat", "")); + CompoundTag offlinePlayerData = server.getOfflinePlayerData(uuid, false); + convertInventory(offlinePlayerData); + String s = name2uuid.get(uuid); + if (s == null || s.isBlank()) { + System.out.println(uuid + "is null"); + continue; + } + 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); + 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 { @@ -89,6 +131,7 @@ public static void convertInventory(CompoundTag root) { int slot = nbt.getByte("Slot"); int count = nbt.getByte("Count"); int damage = nbt.getShort("Damage"); + CompoundTag tag = nbt.contains("tag") ? nbt.getCompound("tag") : null; if (slot >= 9 && slot < 45) { Item item = Item.get(id); @@ -98,8 +141,8 @@ public static void convertInventory(CompoundTag root) { .putShort("Damage", damage); newTag.putString("Name", namespaceId); newTag.putByte("Slot", slot - 9); - if (item.hasCompoundTag()) { - newTag.putCompound("tag", item.getNamedTag()); + if (tag != null) { + newTag.putCompound("tag", tag); } if (item.getBlockUnsafe() != null) { newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe())); @@ -113,8 +156,8 @@ public static void convertInventory(CompoundTag root) { .putShort("Damage", damage); newTag.putString("Name", namespaceId); newTag.putByte("Slot", slot - 64); - if (item.hasCompoundTag()) { - newTag.putCompound("tag", item.getNamedTag()); + if (tag != null) { + newTag.putCompound("tag", tag); } if (item.getBlockUnsafe() != null) { newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe())); @@ -128,8 +171,8 @@ public static void convertInventory(CompoundTag root) { .putShort("Damage", damage); offHand.putString("Name", namespaceId); offHand.putByte("Slot", 0); - if (item.hasCompoundTag()) { - offHand.putCompound("tag", item.getNamedTag()); + if (tag != null) { + offHand.putCompound("tag", tag); } if (item.getBlockUnsafe() != null) { offHand.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe())); @@ -150,17 +193,19 @@ public static void convertInventory(CompoundTag root) { int slot = nbt.getByte("Slot"); int count = nbt.getByte("Count"); int damage = nbt.getShort("Damage"); + CompoundTag tag = nbt.contains("tag") ? nbt.getCompound("tag") : null; if (slot >= 0 && slot < 27) { Item item = Item.get(id); String namespaceId = item.getNamespaceId(); + if (namespaceId == null || namespaceId.isBlank()) continue; 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 (tag != null) { + newTag.putCompound("tag", tag); } if (item.getBlockUnsafe() != null) { newTag.putCompound("Block", NBTIO.putBlockHelper(item.getBlockUnsafe())); diff --git a/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java b/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java index 76f8190bd..6cc0f23af 100644 --- a/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java +++ b/src/main/java/cn/nukkit/convert/leveldb/LevelDBChunkSerializer.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityChest; import cn.nukkit.blockstate.BlockState; import cn.nukkit.blockstate.BlockStateRegistry; import cn.nukkit.convert.BlockEntityConvert; @@ -140,8 +141,9 @@ private void serializeTileAndEntity(WriteBatch writeBatch, Chunk chunk, Dimensio if (blockEntities.isEmpty()) writeBatch.delete(key); else { for (BlockEntity blockEntity : blockEntities) { + if (blockEntity instanceof BlockEntityChest chest) chest.checkPairing(); blockEntity.saveNBT(); - BlockEntityConvert.convertInventory(blockEntity.namedTag); + BlockEntityConvert.convertBlockEntity(blockEntity.namedTag); NBTIO.write(blockEntity.namedTag, bufStream, ByteOrder.LITTLE_ENDIAN); } writeBatch.put(key, Utils.convertByteBuf2Array(tileBuffer));