mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 07:20:00 +08:00
232 lines
11 KiB
Diff
232 lines
11 KiB
Diff
|
From b33d52f15d20e9c6ba4457eb799b67e32792e595 Mon Sep 17 00:00:00 2001
|
||
|
From: Aikar <aikar@aikar.co>
|
||
|
Date: Sun, 29 Jul 2018 15:48:50 -0400
|
||
|
Subject: [PATCH] Provide option to use a versioned world folder for testing
|
||
|
|
||
|
This should not ever be used in production!!
|
||
|
|
||
|
This setting is intended for testing so you can try out converting your world
|
||
|
without actually modifying the world files.
|
||
|
|
||
|
This will add some additional overhead to your world, but you're
|
||
|
just testing anyways so that's not a big deal :)
|
||
|
|
||
|
Will store in a folder named after the current version.
|
||
|
|
||
|
PlayerData and Data folders are copied on server start, so there
|
||
|
may be some delay there, but region files are only copied on demand.
|
||
|
|
||
|
This is highly experiemental so backup your world before relying on this to not modify it
|
||
|
|
||
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||
|
index 5a17ce3d22..f7ece47ed9 100644
|
||
|
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||
|
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||
|
@@ -13,6 +13,7 @@ import java.util.List;
|
||
|
import java.util.Map;
|
||
|
import java.util.concurrent.TimeUnit;
|
||
|
import java.util.logging.Level;
|
||
|
+import java.util.logging.Logger;
|
||
|
import java.util.regex.Pattern;
|
||
|
|
||
|
import com.google.common.collect.Lists;
|
||
|
@@ -303,4 +304,27 @@ public class PaperConfig {
|
||
|
Bukkit.getLogger().log(Level.INFO, "Using Aikar's Alternative Luck Formula to apply Luck attribute to all loot pool calculations. See https://luckformula.emc.gs");
|
||
|
}
|
||
|
}
|
||
|
+
|
||
|
+ public static boolean useVersionedWorld = false;
|
||
|
+ private static void useVersionedWorld() {
|
||
|
+ useVersionedWorld = getBoolean("settings.use-versioned-world", false);
|
||
|
+ if (useVersionedWorld) {
|
||
|
+ Logger logger = Bukkit.getLogger();
|
||
|
+ String ver = MinecraftServer.getServer().getVersion();
|
||
|
+ logger.log(Level.INFO, "******************************************************");
|
||
|
+ logger.log(Level.INFO, "*** Using a versioned world folder. Your world will be saved");
|
||
|
+ logger.log(Level.INFO, "*** to into the " + ver + " folder, but copied from your current world.");
|
||
|
+ logger.log(Level.INFO, "*** ");
|
||
|
+ logger.log(Level.INFO, "*** This setting should not be used in your real world!!!");
|
||
|
+ logger.log(Level.INFO, "*** If you want to retain the new world, you need to move ");
|
||
|
+ logger.log(Level.INFO, "*** the folders out of the " + ver + " folder and overwrite existing");
|
||
|
+ logger.log(Level.INFO, "*** ");
|
||
|
+ logger.log(Level.INFO, "*** Deleting the " + ver + " folder will cause it to recreate again");
|
||
|
+ logger.log(Level.INFO, "*** from your unversioned world files.");
|
||
|
+ logger.log(Level.INFO, "*** ");
|
||
|
+ logger.log(Level.INFO, "*** You should backup your original world files incase something goes");
|
||
|
+ logger.log(Level.INFO, "*** wrong with this system! This is not a backup system.");
|
||
|
+ logger.log(Level.INFO, "******************************************************");
|
||
|
+ }
|
||
|
+ }
|
||
|
}
|
||
|
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||
|
index bd52bf6561..54840851ba 100644
|
||
|
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||
|
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||
|
@@ -55,8 +55,56 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
||
|
private PersistentStructureLegacy e;
|
||
|
// private boolean f; // CraftBukkit
|
||
|
private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot
|
||
|
+ // Paper start - support saving to an alternate directory
|
||
|
+ private final File templateWorld;
|
||
|
+ private final File actualWorld;
|
||
|
+ private final boolean useAltWorld;
|
||
|
+
|
||
|
+ private boolean chunkExists0(int x, int z) {
|
||
|
+ copyIfNeeded(x, z);
|
||
|
+ return RegionFileCache.chunkExists(this.actualWorld, x, z);
|
||
|
+ }
|
||
|
+ private synchronized void copyIfNeeded(int x, int z) {
|
||
|
+ if (!useAltWorld) {
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ if (RegionFileCache.hasRegionFile(this.actualWorld, x, z)) {
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ File actual = RegionFileCache.getRegionFileName(this.actualWorld, x, z);
|
||
|
+ File template = RegionFileCache.getRegionFileName(this.templateWorld, x, z);
|
||
|
+ if (!actual.exists() && template.exists()) {
|
||
|
+ try {
|
||
|
+ //a.info("Copying" + template + " to " + actual);
|
||
|
+ java.nio.file.Files.copy(template.toPath(), actual.toPath(), java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
|
||
|
+ } catch (IOException e1) {
|
||
|
+ LogManager.getLogger().error("Error copying " + template + " to " + actual, e1);
|
||
|
+ MinecraftServer.getServer().safeShutdown();
|
||
|
+ org.spigotmc.SneakyThrow.sneaky(e1);
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
+ }
|
||
|
public ChunkRegionLoader(File file, DataFixer datafixer) {
|
||
|
+ // Paper
|
||
|
+ this.actualWorld = file;
|
||
|
+ if (com.destroystokyo.paper.PaperConfig.useVersionedWorld) {
|
||
|
+ this.useAltWorld = true;
|
||
|
+ String name = file.getName();
|
||
|
+ File container = file.getParentFile().getParentFile();
|
||
|
+ if (name.equals("DIM-1") || name.equals("DIM1")) {
|
||
|
+ container = container.getParentFile();
|
||
|
+ }
|
||
|
+ this.templateWorld = new File(container, name);
|
||
|
+ File region = new File(file, "region");
|
||
|
+ if (!region.exists()) {
|
||
|
+ region.mkdirs();
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ this.useAltWorld = false;
|
||
|
+ this.templateWorld = file;
|
||
|
+ }
|
||
|
+ // Paper end
|
||
|
this.c = file;
|
||
|
this.d = datafixer;
|
||
|
}
|
||
|
@@ -77,7 +125,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- if (RegionFileCache.chunkExists(this.c, x, z)) {
|
||
|
+ if (chunkExists0(x, z)) { // Paper
|
||
|
NBTTagCompound nbt = RegionFileCache.read(this.c, x, z);
|
||
|
if (nbt != null) {
|
||
|
NBTTagCompound level = nbt.getCompound("Level");
|
||
|
@@ -97,6 +145,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
||
|
|
||
|
@Nullable
|
||
|
private NBTTagCompound a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection, int i, int j, @Nullable GeneratorAccess generatoraccess) throws IOException {
|
||
|
+ copyIfNeeded(i, j); // Paper
|
||
|
NBTTagCompound nbttagcompound = RegionFileCache.read(this.c, i, j);
|
||
|
|
||
|
if (nbttagcompound == null) {
|
||
|
@@ -205,7 +254,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
||
|
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
|
||
|
Supplier<NBTTagCompound> nbttagcompound = this.b.get(chunkcoordintpair); // Spigot
|
||
|
|
||
|
- return nbttagcompound != null ? true : RegionFileCache.chunkExists(this.c, i, j);
|
||
|
+ return nbttagcompound != null ? true : chunkExists0(i, j); // Paper
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java
|
||
|
index 609d6c3550..f1fa7c7177 100644
|
||
|
--- a/src/main/java/net/minecraft/server/RegionFileCache.java
|
||
|
+++ b/src/main/java/net/minecraft/server/RegionFileCache.java
|
||
|
@@ -74,6 +74,13 @@ public class RegionFileCache {
|
||
|
itr.remove();
|
||
|
}
|
||
|
}
|
||
|
+ public static synchronized File getRegionFileName(File file, int i, int j) {
|
||
|
+ File file1 = new File(file, "region");
|
||
|
+ return new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
|
||
|
+ }
|
||
|
+ public static synchronized boolean hasRegionFile(File file, int i, int j) {
|
||
|
+ return RegionFileCache.a.containsKey(getRegionFileName(file, i, j));
|
||
|
+ }
|
||
|
// Paper End
|
||
|
|
||
|
public static synchronized void a() {
|
||
|
diff --git a/src/main/java/net/minecraft/server/WorldNBTStorage.java b/src/main/java/net/minecraft/server/WorldNBTStorage.java
|
||
|
index 9a243010d0..3fabbe94f9 100644
|
||
|
--- a/src/main/java/net/minecraft/server/WorldNBTStorage.java
|
||
|
+++ b/src/main/java/net/minecraft/server/WorldNBTStorage.java
|
||
|
@@ -33,6 +33,58 @@ public class WorldNBTStorage implements IDataManager, IPlayerFileData {
|
||
|
|
||
|
public WorldNBTStorage(File file, String s, @Nullable MinecraftServer minecraftserver, DataFixer datafixer) {
|
||
|
this.a = datafixer;
|
||
|
+ // Paper start
|
||
|
+ if (com.destroystokyo.paper.PaperConfig.useVersionedWorld) {
|
||
|
+ File origBaseDir = new File(file, s);
|
||
|
+ final String currentVersion = MinecraftServer.getServer().getVersion();
|
||
|
+ file = new File(file, currentVersion);
|
||
|
+ File baseDir = new File(file, s);
|
||
|
+
|
||
|
+ if (!baseDir.exists() && origBaseDir.exists() && !baseDir.mkdirs()) {
|
||
|
+ LogManager.getLogger().error("Could not create world directory for " + file);
|
||
|
+ System.exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ try {
|
||
|
+ boolean printedHeader = false;
|
||
|
+ String[] dirs = {"advancements", "data", "datapacks", "playerdata", "stats"};
|
||
|
+ for (String dir : dirs) {
|
||
|
+ File origPlayerData = new File(origBaseDir, dir);
|
||
|
+ File targetPlayerData = new File(baseDir, dir);
|
||
|
+ if (origPlayerData.exists() && !targetPlayerData.exists()) {
|
||
|
+ if (!printedHeader) {
|
||
|
+ LogManager.getLogger().info("**** VERSIONED WORLD - Copying files");
|
||
|
+ printedHeader = true;
|
||
|
+ }
|
||
|
+ LogManager.getLogger().info("- Copying: " + dir);
|
||
|
+ org.apache.commons.io.FileUtils.copyDirectory(origPlayerData, targetPlayerData);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ String[] files = {"level.dat", "level.dat_old", "session.lock", "uid.dat"};
|
||
|
+ for (String fileName : files) {
|
||
|
+ File origPlayerData = new File(origBaseDir, fileName);
|
||
|
+ File targetPlayerData = new File(baseDir, fileName);
|
||
|
+ if (origPlayerData.exists() && !targetPlayerData.exists()) {
|
||
|
+ if (!printedHeader) {
|
||
|
+ LogManager.getLogger().info("- Copying files");
|
||
|
+ printedHeader = true;
|
||
|
+ }
|
||
|
+ LogManager.getLogger().info("- Copying: " + fileName);
|
||
|
+ org.apache.commons.io.FileUtils.copyFile(origPlayerData, targetPlayerData);
|
||
|
+
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (printedHeader) {
|
||
|
+ LogManager.getLogger().info("**** VERSIONED WORLD - Copying DONE");
|
||
|
+ }
|
||
|
+ } catch (IOException e) {
|
||
|
+ LogManager.getLogger().error("Error copying versioned world data for " + origBaseDir + " to " + baseDir, e);
|
||
|
+ org.spigotmc.SneakyThrow.sneaky(e);
|
||
|
+ }
|
||
|
+
|
||
|
+ }
|
||
|
+ // Paper end
|
||
|
this.baseDir = new File(file, s);
|
||
|
this.baseDir.mkdirs();
|
||
|
this.playerDir = new File(this.baseDir, "playerdata");
|
||
|
--
|
||
|
2.18.0
|
||
|
|