1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
|