Skip to content

Commit

Permalink
feat: complete world convert
Browse files Browse the repository at this point in the history
  • Loading branch information
CoolLoong committed Mar 2, 2024
1 parent f584438 commit 69db088
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 8 deletions.
4 changes: 1 addition & 3 deletions src/main/java/cn/nukkit/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public Level remove(Object key) {

private Watchdog watchdog;

private DB nameLookup;
public DB nameLookup;

private PlayerDataSerializer playerDataSerializer;

Expand Down Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/cn/nukkit/convert/BlockEntityConvert.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
14 changes: 9 additions & 5 deletions src/main/java/cn/nukkit/convert/Convert.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
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;
import java.io.IOException;
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 {
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down
175 changes: 175 additions & 0 deletions src/main/java/cn/nukkit/convert/PlayerDataConvert.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions src/main/java/cn/nukkit/convert/task/ConvertTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/cn/nukkit/nbt/tag/ByteArrayTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -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, ");
Expand Down

0 comments on commit 69db088

Please sign in to comment.