Fix and optimize legacy world conversion (#6473)

CraftBukkit breaks legacy world conversion in three ways:
- Writes userdata to the path of the userdata folder rather than to
  the correct file inside the aforementioned folder. This causes the
  userdata folder to fail to be created as a file already exists at
  its path.
- Makes changes to how multiworld works, without modifying
  McRegionUpgrader to be aware of these changes.
- Calls methods on Bukkit before the server is initialized.

This patch fixes all of these issues, and also threads the
McRegionUpgrader to improve performance.
This commit is contained in:
Jason 2021-08-28 00:07:12 -05:00 committed by GitHub
parent 1c4f87a258
commit 411a5eed18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,178 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Sun, 22 Aug 2021 23:03:33 -0700
Subject: [PATCH] Fix and optimize legacy world conversion
CraftBukkit breaks legacy world conversion in three ways:
- Writes userdata to the path of the userdata folder rather than to
the correct file inside the aforementioned folder. This causes the
userdata folder to fail to be created as a file already exists at
its path.
- Makes changes to how multiworld works, without modifying
McRegionUpgrader to be aware of these changes.
- Calls methods on Bukkit before the server is initialized.
This patch fixes all of these issues, and also threads the
McRegionUpgrader to improve performance.
diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
index 8703f97dc2f392b136c6851aa09b607cbfdfa5de..ade010fe3b62a4624b009c6d665e9909b2d314ac 100644
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
@@ -355,14 +355,14 @@ public class OldUsersConverter {
}
private void movePlayerFile(File playerDataFolder, String fileName, String uuid) {
- File file5 = new File(file, fileName + ".dat");
+ File file5 = new File(file, fileName + ".dat"); // Paper - diff on change
File file6 = new File(playerDataFolder, uuid + ".dat");
// CraftBukkit start - Use old file name to seed lastKnownName
CompoundTag root = null;
try {
- root = NbtIo.readCompressed(new java.io.FileInputStream(file5));
+ root = NbtIo.readCompressed(new java.io.FileInputStream(file5)); // Paper - diff on change
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
@@ -376,7 +376,7 @@ public class OldUsersConverter {
data.putString("lastKnownName", fileName);
try {
- NbtIo.writeCompressed(root, new java.io.FileOutputStream(file2));
+ NbtIo.writeCompressed(root, new java.io.FileOutputStream(file5)); // Paper - write to correct path (diff on change)
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
diff --git a/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java b/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java
index af8a555c777b5abbaa2615d2ff03f03a9a93847e..b794c02ea36bdc901b1f6a160095abb3fcfe9b60 100644
--- a/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java
+++ b/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java
@@ -348,6 +348,12 @@ public class LevelStorageSource {
});
}
+ // Paper start - copied from vanilla before below CB diff
+ public File getDimensionPathForLegacyConversion(ResourceKey<Level> key) {
+ return DimensionType.getStorageFolder(key, this.levelPath.toFile());
+ }
+ // Paper end
+
public File getDimensionPath(ResourceKey<Level> key) {
return LevelStorageSource.getFolder(this.levelPath.toFile(), this.dimensionType); // CraftBukkit
}
diff --git a/src/main/java/net/minecraft/world/level/storage/McRegionUpgrader.java b/src/main/java/net/minecraft/world/level/storage/McRegionUpgrader.java
index 87705fc8ee32016bf5ca533eb278bf32df08d3a3..b9bcbcf509ebb59d15082ce0faef8e405c16bdcc 100644
--- a/src/main/java/net/minecraft/world/level/storage/McRegionUpgrader.java
+++ b/src/main/java/net/minecraft/world/level/storage/McRegionUpgrader.java
@@ -35,13 +35,29 @@ public class McRegionUpgrader {
private static final String MCREGION_EXTENSION = ".mcr";
static boolean convertLevel(LevelStorageSource.LevelStorageAccess storageSession, ProgressListener progressListener) {
+ // Paper start
+ progressListener = new ProgressListener() {
+ @Override
+ public void progressStartNoAbort(net.minecraft.network.chat.Component title) {}
+ @Override
+ public void progressStart(net.minecraft.network.chat.Component title) {}
+ @Override
+ public void progressStage(net.minecraft.network.chat.Component task) {}
+ @Override
+ public void progressStagePercentage(int percentage) {}
+ @Override
+ public void stop() {}
+ };
+ // Paper end
progressListener.progressStagePercentage(0);
List<File> list = Lists.newArrayList();
List<File> list2 = Lists.newArrayList();
List<File> list3 = Lists.newArrayList();
- File file = storageSession.getDimensionPath(Level.OVERWORLD);
- File file2 = storageSession.getDimensionPath(Level.NETHER);
- File file3 = storageSession.getDimensionPath(Level.END);
+ // Paper start
+ File file = storageSession.getDimensionPathForLegacyConversion(Level.OVERWORLD);
+ File file2 = storageSession.getDimensionPathForLegacyConversion(Level.NETHER);
+ File file3 = storageSession.getDimensionPathForLegacyConversion(Level.END);
+ // Paper end
LOGGER.info("Scanning folders...");
addRegionFiles(file, list);
if (file2.exists()) {
@@ -88,14 +104,58 @@ public class McRegionUpgrader {
}
private static void convertRegions(RegistryAccess.RegistryHolder registryManager, File directory, Iterable<File> files, BiomeSource biomeSource, int i, int j, ProgressListener progressListener) {
- for(File file : files) {
- convertRegion(registryManager, directory, file, biomeSource, i, j, progressListener);
- ++i;
- int k = (int)Math.round(100.0D * (double)i / (double)j);
- progressListener.progressStagePercentage(k);
+ // Paper start - thread this because it's dead simple
+ convertRegionsThreaded(registryManager, directory, files, biomeSource, i, j, progressListener);
+ }
+
+ private static void convertRegionsThreaded(RegistryAccess.RegistryHolder registryManager, File directory, Iterable<File> files, BiomeSource biomeSource, int progress, int total, ProgressListener progressListener) {
+ if (!files.iterator().hasNext()) {
+ return;
}
+ final int threads = Runtime.getRuntime().availableProcessors() / 2;
+ final java.util.concurrent.ExecutorService threadPool = java.util.concurrent.Executors.newFixedThreadPool(Math.max(1, threads), new java.util.concurrent.ThreadFactory() {
+ private final java.util.concurrent.atomic.AtomicInteger threadCounter = new java.util.concurrent.atomic.AtomicInteger();
+
+ @Override
+ public Thread newThread(final Runnable run) {
+ final Thread ret = new Thread(run);
+
+ ret.setName("World upgrader thread for directory " + directory + " #" + this.threadCounter.getAndIncrement());
+ ret.setUncaughtExceptionHandler((thread, throwable) -> {
+ LOGGER.fatal("Error upgrading world", throwable);
+ });
+
+ return ret;
+ }
+ });
+ final java.util.concurrent.atomic.AtomicInteger converted = new java.util.concurrent.atomic.AtomicInteger(progress);
+
+ final long start = System.nanoTime();
+
+ for (final File file : files) {
+ threadPool.execute(() -> {
+ convertRegion(registryManager, directory, file, biomeSource, 0, total, progressListener);
+ converted.incrementAndGet();
+ });
+ }
+ threadPool.shutdown();
+
+ final java.text.DecimalFormat format = new java.text.DecimalFormat("#0.00");
+ while (!threadPool.isTerminated()) {
+ final int getConverted = converted.get();
+ LOGGER.info("Converting... {}/{} ({}%)", getConverted, total, format.format(100.0D * (double) getConverted / (double) total));
+ try {
+ Thread.sleep(1000L);
+ } catch (final InterruptedException ignored) {}
+ }
+
+ final long end = System.nanoTime();
+
+ final double durationSeconds = Math.ceil((end - start) * 1.0e-9);
+ LOGGER.info("Conversion for {} completed in {}s", directory, durationSeconds);
}
+ // Paper end
private static void convertRegion(RegistryAccess.RegistryHolder registryManager, File directory, File file, BiomeSource biomeSource, int i, int j, ProgressListener progressListener) {
String string = file.getName();
diff --git a/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java b/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java
index 4d0af984490b556a9911c3b8fdca1e168e6fe932..c20e5d69b4ad8adcdaffb56e4e2a24596ae16edf 100644
--- a/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java
+++ b/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java
@@ -212,7 +212,7 @@ public class PrimaryLevelData implements ServerLevelData, WorldData {
levelTag.putUUID("WanderingTraderId", this.wanderingTraderId);
}
- levelTag.putString("Bukkit.Version", Bukkit.getName() + "/" + Bukkit.getVersion() + "/" + Bukkit.getBukkitVersion()); // CraftBukkit
+ if (Bukkit.getServer() != null) levelTag.putString("Bukkit.Version", Bukkit.getName() + "/" + Bukkit.getVersion() + "/" + Bukkit.getBukkitVersion()); // CraftBukkit // Paper - server may not be started yet
}
@Override