Skip to content

Instantly share code, notes, and snippets.

@CyberFlameGO
Forked from HeathLoganCampbell/!Extra info.md
Created October 10, 2021 23:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CyberFlameGO/04b3f7d2ee4ab5d4cf7fa1e295e041c6 to your computer and use it in GitHub Desktop.
Save CyberFlameGO/04b3f7d2ee4ab5d4cf7fa1e295e041c6 to your computer and use it in GitHub Desktop.
A weekend project to allow minecraft worlds to be saved in a mysql database, mysql worlds. This was done on top of Paper 1.17 on fa53a1a7f

SkyPaper

Uploading and download Minecraft worlds from mysql

The idea was to be able to upload worlds to a database so that we could have multiple servers upload and download them at will to enable load balancing for servers such as skyblock as the demands of scalable 1.17 servers grow while the performance falls version to version.

The important idea here was to see if it was possible to upload worlds to a database, which the answer is yes :D. but this even works better for skyblock servers as they are mostly air, which allowed us to do a small optimization. which is when a chunk is empty, we don't have it, as generating empty chunks is cheap as chips, but storing them has about 500B of overhead. so it ends up taking longer to fetch them from the database, then just generate them.

There is a lot more that could be done, but of course the trade of for a scale of 1000s of players doesn't really make sense but for the sack of fun, a few things that could be done is...

  • Use a different compression algorithm
  • Use a better data format instead of minecraft's default
  • Remake the LoadWorld method to skip a few steps
  • Make the database get hit less per world load (seems to be 3 just to get the level.dat)

How to Use?

fork https://github.com/PaperMC/Paper at fa53a1a7f drop in the patch file in the patches/server/ folder follow Papers steps on how to compile Update SkyPaper.java with your database infomation or make a config for it

Tables

  • level.dat -> worldproperties
  • uid.dat -> world
  • region/ -> worldchunk_region
  • poi/ -> worldchunk_poi
  • entities/ -> worldchunk_entities
  • data/ -> worlddatacache

Note

Why not just upload a zip of the world folder, this is because the chunk size of that zip file is not chunkable and can easily get overgrown so we'll be transfering a lot of needless data. this way we have more control and less overheads

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Heath <Github@HeathLoganCampbell.com>
Date: Sun, 10 Oct 2021 23:31:38 +1300
Subject: [PATCH] [SkyPaper-001] Databased Worlds
It works pretty simply by making direct mapping from files to database.
diff --git a/build.gradle.kts b/build.gradle.kts
index d80cfcb94db51440b5d0aa589a9a3d8a4189a9aa..afef0c4ef696897232e795b40bff2cf52516767a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -61,6 +61,7 @@ dependencies {
implementation("co.aikar:cleaner:1.0-SNAPSHOT") // Paper
implementation("io.netty:netty-all:4.1.65.Final") // Paper
+ implementation("com.zaxxer:HikariCP:5.0.0") // SkySpigot
implementation("org.quiltmc:tiny-mappings-parser:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
implementation("com.velocitypowered:velocity-native:1.1.0-SNAPSHOT") // Paper
diff --git a/src/main/java/dev/cobblesword/skypaper/SkyPaper.java b/src/main/java/dev/cobblesword/skypaper/SkyPaper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3e4621a579d3b39f5fb2f8051e789bc3c2d8a31
--- /dev/null
+++ b/src/main/java/dev/cobblesword/skypaper/SkyPaper.java
@@ -0,0 +1,74 @@
+package dev.cobblesword.skypaper;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import dev.cobblesword.skypaper.database.WorldChunkedDataDao;
+import dev.cobblesword.skypaper.database.WorldDao;
+import dev.cobblesword.skypaper.database.WorldDataCacheDao;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public class SkyPaper
+{
+ public static SkyPaper INSTANCE = new SkyPaper();
+
+ private HikariDataSource dataSource;
+
+ private WorldDao worldDao;
+ private WorldDataCacheDao worldDataCacheDao;
+ private WorldChunkedDataDao worldChunkDao;
+
+ public SkyPaper()
+ {
+ }
+
+ public void onEnable()
+ {
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl("jdbc:mysql://localhost:3306/skyblock");
+ config.setUsername("root");
+ config.setPassword("root");
+ config.addDataSourceProperty("cachePrepStmts", "true");
+ config.addDataSourceProperty("prepStmtCacheSize", "250");
+ config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
+
+ dataSource = new HikariDataSource(config);
+
+ this.worldDao = new WorldDao();
+ this.worldDataCacheDao = new WorldDataCacheDao();
+ this.worldChunkDao = new WorldChunkedDataDao();
+
+ org.spigotmc.SpigotConfig.disablePlayerDataSaving = true;
+ }
+
+ public void onDisable()
+ {
+
+ }
+
+ public Connection getConnection() throws SQLException
+ {
+ return dataSource.getConnection();
+ }
+
+ public WorldDao getWorldDao()
+ {
+ return this.worldDao;
+ }
+
+ public WorldDataCacheDao getWorldDataCacheDao()
+ {
+ return this.worldDataCacheDao;
+ }
+
+ public WorldChunkedDataDao getWorldChunkDao()
+ {
+ return this.worldChunkDao;
+ }
+
+ public static boolean isDataPacksDisabled()
+ {
+ return true;
+ }
+}
diff --git a/src/main/java/dev/cobblesword/skypaper/database/WorldChunkedDataDao.java b/src/main/java/dev/cobblesword/skypaper/database/WorldChunkedDataDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebcd36ecbec33056f2e82d957113ab435335cf79
--- /dev/null
+++ b/src/main/java/dev/cobblesword/skypaper/database/WorldChunkedDataDao.java
@@ -0,0 +1,231 @@
+package dev.cobblesword.skypaper.database;
+
+import ca.spottedleaf.dataconverter.types.ObjectType;
+import dev.cobblesword.skypaper.SkyPaper;
+import dev.cobblesword.skypaper.database.dto.WorldDTO;
+import net.minecraft.nbt.*;
+import net.minecraft.world.level.ChunkPos;
+
+import javax.sql.rowset.serial.SerialBlob;
+import java.io.*;
+import java.sql.*;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class WorldChunkedDataDao
+{
+ public CompoundTag loadWorldChunk(String dataType, int worldDatabaseId, ChunkPos chunkPos)
+ {
+ long chunkIndex = chunkPos.toLong();
+
+ Connection connection = null;
+ PreparedStatement prepareStatement = null;
+ try
+ {
+ connection = SkyPaper.INSTANCE.getConnection();
+ prepareStatement = connection.prepareStatement("SELECT * FROM `worldchunk_" + dataType + "` WHERE `world_id` = ? AND `chunk_index` = ?");
+ prepareStatement.setInt(1, worldDatabaseId);
+ prepareStatement.setLong(2, chunkIndex);
+ ResultSet rs = prepareStatement.executeQuery();
+
+ if (!rs.next()) {
+ // no chunk found
+ return null;
+ }
+
+ Blob nbt = rs.getBlob("nbt");
+
+ CompoundTag nbttagcompound;
+
+ InputStream binaryStream = nbt.getBinaryStream();
+ DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(binaryStream)));
+
+ try
+ {
+ nbttagcompound = NbtIo.read(datainputstream, NbtAccounter.UNLIMITED);
+ }
+ catch (Throwable throwable)
+ {
+ try
+ {
+ binaryStream.close();
+ datainputstream.close();
+ }
+ catch (Throwable throwable1)
+ {
+ throwable.addSuppressed(throwable1);
+ }
+
+ throw throwable;
+ }
+
+ datainputstream.close();
+ binaryStream.close();
+ return nbttagcompound;
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
+ try {
+ if(connection != null && !connection.isClosed()) connection.close();
+ if(prepareStatement != null && !prepareStatement.isClosed()) prepareStatement.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isEmptyChunk(CompoundTag compound)
+ {
+ CompoundTag level = compound.getCompound("Level");
+ ListTag sections = level.getList("Sections", 10);
+ CompoundTag tileEntities = level.getCompound("TileEntities");
+ CompoundTag entities = level.getCompound("Entities");
+
+ for (int i = 0; i < sections.size(); i++)
+ {
+ CompoundTag section = sections.getCompound(i);
+ ListTag palette = section.getList("Palette", 10);
+ if(palette.size() > 1)
+ {
+ // contains more than just air
+ return false;
+ }
+
+ for (int j = 0; j < palette.size(); j++)
+ {
+ CompoundTag paletteBlock = palette.getCompound(j);
+ String blockType = paletteBlock.getString("Name");
+ if(!blockType.equals("minecraft:air"))
+ {
+ return false;
+ }
+ }
+ }
+
+ if(tileEntities.size() == 0 && entities.size() == 0)
+ {
+ // Don't save chunks with no meaningful information, in this case it'll only be saving biome data
+ return true;
+ }
+
+ return false;
+ }
+
+ public void saveWorldChunk(String dataType, int worldDatabaseId, ChunkPos pos, CompoundTag compound)
+ {
+ if(compound == null)
+ {
+ return;
+ }
+
+ long chunkIndex = pos.toLong();
+
+ if(dataType.equals("region"))
+ {
+ // Don't save empty chunks
+ if(isEmptyChunk(compound))
+ {
+ return;
+ }
+ }
+
+ try {
+ Connection connection = SkyPaper.INSTANCE.getConnection();
+
+ PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO `worldchunk_" + dataType + "`(`id`, `world_id`, `chunk_x`, `chunk_z`, `chunk_index`, `nbt`) VALUES (NULL, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE nbt = ?");
+ ByteArrayOutputStream stream = null;
+ DataOutputStream dataoutputstream = null;
+
+ Blob blob = null;
+
+ try {
+ stream = new ByteArrayOutputStream();
+ BufferedOutputStream bufferOut = new BufferedOutputStream(new GZIPOutputStream(stream));
+ dataoutputstream = new DataOutputStream(bufferOut);
+ DataOutput output = dataoutputstream;
+
+ output.writeByte(compound.getId());
+ if (compound.getId() != 0)
+ {
+ output.writeUTF("");
+ compound.write(output);
+ }
+
+ dataoutputstream.close();
+ blob = new SerialBlob(stream.toByteArray());
+ }
+ catch (Throwable throwable)
+ {
+ throw throwable;
+ }
+ finally
+ {
+ if(stream != null) stream.close();
+ if(dataoutputstream != null) dataoutputstream.close();
+ }
+
+ prepareStatement.setInt(1, worldDatabaseId);
+ prepareStatement.setInt(2, pos.x);
+ prepareStatement.setInt(3, pos.z);
+ prepareStatement.setLong(4, chunkIndex);
+ prepareStatement.setBlob(5, blob);
+ prepareStatement.setBlob(6, blob);
+
+ prepareStatement.executeUpdate();
+ prepareStatement.close();
+ connection.close();
+ } catch (SQLException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public WorldDTO loadOrCreateUID(String levelId)
+ {
+ Connection connection = null;
+ PreparedStatement prepareStatement = null;
+ try {
+ connection = SkyPaper.INSTANCE.getConnection();
+
+ prepareStatement = connection.prepareStatement("SELECT * FROM world WHERE level_id = ?");
+ prepareStatement.setString(1, levelId);
+ ResultSet resultSet = prepareStatement.executeQuery();
+ if (resultSet.next())
+ {
+ int databaseId = resultSet.getInt("id");
+ String uid = resultSet.getString("uid");
+ return new WorldDTO(databaseId, UUID.fromString(uid));
+ }
+
+ UUID newUID = UUID.randomUUID();
+
+ prepareStatement = connection.prepareStatement("INSERT INTO `world`(`id`, `level_id`, `uid`) VALUES (NULL, ?, ?)", Statement.RETURN_GENERATED_KEYS);
+ prepareStatement.setString(1, levelId);
+ prepareStatement.setString(2, newUID.toString());
+ prepareStatement.executeUpdate();
+
+ ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
+ int databaseId = generatedKeys.getInt(1);
+
+ return new WorldDTO(databaseId, newUID);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ prepareStatement.close();
+ connection.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/dev/cobblesword/skypaper/database/WorldDao.java b/src/main/java/dev/cobblesword/skypaper/database/WorldDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..21c835db3f53a36757dd98ef7449232c1e7b8c14
--- /dev/null
+++ b/src/main/java/dev/cobblesword/skypaper/database/WorldDao.java
@@ -0,0 +1,175 @@
+package dev.cobblesword.skypaper.database;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import dev.cobblesword.skypaper.database.dto.WorldDTO;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtAccounter;
+import net.minecraft.nbt.NbtIo;
+import org.apache.commons.lang3.tuple.Pair;
+
+import javax.sql.rowset.serial.SerialBlob;
+import java.io.*;
+import java.sql.*;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class WorldDao
+{
+ public CompoundTag loadWorldData(String levelId)
+ {
+ Connection connection = null;
+ PreparedStatement prepareStatement = null;
+ try
+ {
+ connection = SkyPaper.INSTANCE.getConnection();
+ prepareStatement = connection.prepareStatement("SELECT * FROM worlddata WHERE level_id = ?");
+ prepareStatement.setString(1, levelId);
+ ResultSet rs = prepareStatement.executeQuery();
+
+ if (!rs.next()) {
+ // no worlds found
+ return null;
+ }
+
+ Blob nbt = rs.getBlob("nbt");
+
+ CompoundTag nbttagcompound;
+
+ InputStream binaryStream = nbt.getBinaryStream();
+ DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(binaryStream)));
+
+ try
+ {
+ nbttagcompound = NbtIo.read(datainputstream, NbtAccounter.UNLIMITED);
+ }
+ catch (Throwable throwable)
+ {
+ try
+ {
+ binaryStream.close();
+ datainputstream.close();
+ }
+ catch (Throwable throwable1)
+ {
+ throwable.addSuppressed(throwable1);
+ }
+
+ throw throwable;
+ }
+
+ datainputstream.close();
+ binaryStream.close();
+ return nbttagcompound;
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
+ try {
+ if(connection != null && !connection.isClosed()) connection.close();
+ if(prepareStatement != null && !prepareStatement.isClosed()) prepareStatement.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ public void saveWorldData(String levelId, CompoundTag compound)
+ {
+ try {
+ Connection connection = SkyPaper.INSTANCE.getConnection();
+
+ PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO worlddata (id,level_id,nbt) VALUES (NULL, ?, ?) ON DUPLICATE KEY UPDATE nbt = ?");
+ ByteArrayOutputStream stream = null;
+ DataOutputStream dataoutputstream = null;
+
+ Blob blob = null;
+
+ try {
+ stream = new ByteArrayOutputStream();
+ BufferedOutputStream bufferOut = new BufferedOutputStream(new GZIPOutputStream(stream));
+ dataoutputstream = new DataOutputStream(bufferOut);
+ DataOutput output = dataoutputstream;
+
+ output.writeByte(compound.getId());
+ if (compound.getId() != 0)
+ {
+ output.writeUTF("");
+ compound.write(output);
+ }
+
+ dataoutputstream.close();
+ blob = new SerialBlob(stream.toByteArray());
+ }
+ catch (Throwable throwable)
+ {
+ throw throwable;
+ }
+ finally
+ {
+ if(stream != null) stream.close();
+ if(dataoutputstream != null) dataoutputstream.close();
+ }
+
+ prepareStatement.setString(1, levelId);
+ prepareStatement.setBlob(2, blob);
+ prepareStatement.setBlob(3, blob);
+
+ prepareStatement.executeUpdate();
+ prepareStatement.close();
+ connection.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public WorldDTO loadOrCreateUID(String levelId)
+ {
+ Connection connection = null;
+ PreparedStatement prepareStatement = null;
+ try {
+ connection = SkyPaper.INSTANCE.getConnection();
+
+ prepareStatement = connection.prepareStatement("SELECT * FROM world WHERE level_id = ?");
+ prepareStatement.setString(1, levelId);
+ ResultSet resultSet = prepareStatement.executeQuery();
+ if (resultSet.next())
+ {
+ int databaseId = resultSet.getInt("id");
+ String uid = resultSet.getString("uid");
+ return new WorldDTO(databaseId, UUID.fromString(uid));
+ }
+
+ UUID newUID = UUID.randomUUID();
+
+ prepareStatement = connection.prepareStatement("INSERT INTO `world`(`id`, `level_id`, `uid`) VALUES (NULL, ?, ?)", Statement.RETURN_GENERATED_KEYS);
+ prepareStatement.setString(1, levelId);
+ prepareStatement.setString(2, newUID.toString());
+ prepareStatement.executeUpdate();
+
+ ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
+ generatedKeys.next();
+ int databaseId = generatedKeys.getInt(1);
+ return new WorldDTO(databaseId, newUID);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ prepareStatement.close();
+ connection.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/dev/cobblesword/skypaper/database/WorldDataCacheDao.java b/src/main/java/dev/cobblesword/skypaper/database/WorldDataCacheDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..cceb9295145dd83ce1afe81536dda60966b60419
--- /dev/null
+++ b/src/main/java/dev/cobblesword/skypaper/database/WorldDataCacheDao.java
@@ -0,0 +1,124 @@
+package dev.cobblesword.skypaper.database;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtAccounter;
+import net.minecraft.nbt.NbtIo;
+
+import javax.sql.rowset.serial.SerialBlob;
+import java.io.*;
+import java.sql.*;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class WorldDataCacheDao
+{
+ public CompoundTag loadWorldDataCache(int worldDataId, String fileName)
+ {
+ try
+ {
+ Connection connection = SkyPaper.INSTANCE.getConnection();
+ PreparedStatement prepareStatement = connection.prepareStatement("SELECT * FROM worldproperties WHERE worlddata_id = ? AND file_name = ?");
+ prepareStatement.setInt(1, worldDataId);
+ prepareStatement.setString(2, fileName);
+ ResultSet rs = prepareStatement.executeQuery();
+
+ if (!rs.next()) {
+ // no cache found
+ return null;
+ }
+
+ Blob nbt = rs.getBlob("nbt");
+
+ CompoundTag nbttagcompound;
+
+ InputStream binaryStream = nbt.getBinaryStream();
+ DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(binaryStream)));
+
+ try
+ {
+ nbttagcompound = NbtIo.read(datainputstream, NbtAccounter.UNLIMITED);
+ }
+ catch (Throwable throwable)
+ {
+ try
+ {
+ binaryStream.close();
+ datainputstream.close();
+ }
+ catch (Throwable throwable1)
+ {
+ throwable.addSuppressed(throwable1);
+ }
+
+ throw throwable;
+ }
+
+ datainputstream.close();
+ binaryStream.close();
+ return nbttagcompound;
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public void saveWorldDataCache(int worldId, String fileName, CompoundTag compound)
+ {
+ try {
+ Connection connection = SkyPaper.INSTANCE.getConnection();
+
+ PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO `worldproperties`(`id`, `worlddata_id`, `file_name`, `nbt`, `update_timestamp`) VALUES (NULL, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE nbt = ?, update_timestamp = ?");
+ ByteArrayOutputStream stream = null;
+ DataOutputStream dataoutputstream = null;
+
+ Blob blob = null;
+
+ try {
+ stream = new ByteArrayOutputStream();
+ BufferedOutputStream bufferOut = new BufferedOutputStream(new GZIPOutputStream(stream));
+ dataoutputstream = new DataOutputStream(bufferOut);
+ DataOutput output = dataoutputstream;
+
+ output.writeByte(compound.getId());
+ if (compound.getId() != 0)
+ {
+ output.writeUTF("");
+ compound.write(output);
+ }
+
+ dataoutputstream.close();
+ blob = new SerialBlob(stream.toByteArray());
+ }
+ catch (Throwable throwable)
+ {
+ throw throwable;
+ }
+ finally
+ {
+ if(stream != null) stream.close();
+ if(dataoutputstream != null) dataoutputstream.close();
+ }
+
+ Timestamp currentTimestamp = new Timestamp(System.currentTimeMillis());
+
+ prepareStatement.setInt(1, worldId);
+ prepareStatement.setString(2, fileName);
+ prepareStatement.setBlob(3, blob);
+ prepareStatement.setTimestamp(4, currentTimestamp);
+ prepareStatement.setBlob(5, blob);
+ prepareStatement.setTimestamp(6, currentTimestamp);
+
+ prepareStatement.executeUpdate();
+ prepareStatement.close();
+ connection.close();
+ } catch (SQLException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/dev/cobblesword/skypaper/database/dto/WorldDTO.java b/src/main/java/dev/cobblesword/skypaper/database/dto/WorldDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b492b1d1cd254e9b8ba06f34d469e3bc4e5fea2
--- /dev/null
+++ b/src/main/java/dev/cobblesword/skypaper/database/dto/WorldDTO.java
@@ -0,0 +1,14 @@
+package dev.cobblesword.skypaper.database.dto;
+
+import java.util.UUID;
+
+public class WorldDTO
+{
+ public int databaseId;
+ public UUID uuid;
+
+ public WorldDTO(int databaseId, UUID uuid) {
+ this.databaseId = databaseId;
+ this.uuid = uuid;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
index 389faf099ff64bb2845222600552c8fc2c83485f..78767ccfce7b9eaffd7825eef5e0763e8fcacf2a 100644
--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
@@ -1,6 +1,8 @@
package io.papermc.paper.world;
import com.mojang.datafixers.DataFixer;
+import dev.cobblesword.skypaper.SkyPaper;
+import dev.cobblesword.skypaper.database.dto.WorldDTO;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
@@ -63,7 +65,8 @@ public class ThreadedWorldUpgrader {
public void convert() {
final File worldFolder = LevelStorageSource.getFolder(this.worldDir, this.dimensionType);
- final DimensionDataStorage worldPersistentData = new DimensionDataStorage(new File(worldFolder, "data"), this.dataFixer);
+ WorldDTO worldDTO = SkyPaper.INSTANCE.getWorldDao().loadOrCreateUID(this.worldDir.getName());
+ final DimensionDataStorage worldPersistentData = new DimensionDataStorage(worldDTO.databaseId, new File(worldFolder, "data"), this.dataFixer);
final File regionFolder = new File(worldFolder, "region");
@@ -80,7 +83,7 @@ public class ThreadedWorldUpgrader {
LOGGER.info("Starting conversion now for world " + this.worldName);
final WorldInfo info = new WorldInfo(() -> worldPersistentData,
- new ChunkStorage(regionFolder, this.dataFixer, false), this.removeCaches, this.worldKey);
+ new ChunkStorage(worldDTO.databaseId, regionFolder, this.dataFixer, false), this.removeCaches, this.worldKey);
long expectedChunks = (long)regionFiles.length * (32L * 32L);
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index cfd43069ee2b6f79afb12e10d223f6bf75100034..4ae3d858fed8c12d80a1e9335f6f608ef3637857 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
@@ -5,8 +5,7 @@ import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.datafixers.DataFixer;
-import com.mojang.serialization.DynamicOps;
-import com.mojang.serialization.Lifecycle;
+
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.net.Proxy;
@@ -15,37 +14,28 @@ import java.nio.file.Paths;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier;
+
+import dev.cobblesword.skypaper.SkyPaper;
import io.papermc.paper.world.ThreadedWorldUpgrader;
-import joptsimple.NonOptionArgumentSpec;
-import joptsimple.OptionParser;
import joptsimple.OptionSet;
-import joptsimple.OptionSpec;
import net.minecraft.CrashReport;
-import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.commands.Commands;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.chat.Component;
import net.minecraft.obfuscate.DontObfuscate;
-import net.minecraft.resources.RegistryReadOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.dedicated.DedicatedServer;
-import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.dedicated.DedicatedServerSettings;
import net.minecraft.server.level.progress.LoggerChunkProgressListener;
import net.minecraft.server.packs.PackType;
-import net.minecraft.server.packs.repository.FolderRepositorySource;
-import net.minecraft.server.packs.repository.PackRepository;
-import net.minecraft.server.packs.repository.PackSource;
-import net.minecraft.server.packs.repository.RepositorySource;
-import net.minecraft.server.packs.repository.ServerPacksSource;
+import net.minecraft.server.packs.repository.*;
import net.minecraft.server.players.GameProfileCache;
import net.minecraft.util.Mth;
import net.minecraft.util.datafix.DataFixers;
import net.minecraft.util.worldupdate.WorldUpgrader;
import net.minecraft.world.level.DataPackConfig;
-import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.storage.LevelResource;
@@ -55,7 +45,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
-import net.minecraft.SharedConstants;
+
public class Main {
@@ -140,15 +130,16 @@ public class Main {
} else {
file = new File(bukkitConfiguration.getString("settings.world-container", "."));
}
+ SkyPaper.INSTANCE.onEnable();
// Paper end - fix SPIGOT-5824
YggdrasilAuthenticationService yggdrasilauthenticationservice = new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY); // Paper
MinecraftSessionService minecraftsessionservice = yggdrasilauthenticationservice.createMinecraftSessionService();
GameProfileRepository gameprofilerepository = yggdrasilauthenticationservice.createProfileRepository();
GameProfileCache usercache = new GameProfileCache(gameprofilerepository, userCacheFile); // Paper - only move usercache.json into folder if --universe is used, not world-container
// CraftBukkit start
- String s = (String) Optional.ofNullable((String) optionset.valueOf("world")).orElse(dedicatedserversettings.getProperties().levelName);
+ String worldName = (String) Optional.ofNullable((String) optionset.valueOf("world")).orElse(dedicatedserversettings.getProperties().levelName);
LevelStorageSource convertable = LevelStorageSource.createDefault(file.toPath());
- LevelStorageSource.LevelStorageAccess convertable_conversionsession = convertable.c(s, LevelStem.OVERWORLD);
+ LevelStorageSource.LevelStorageAccess convertable_conversionsession = convertable.c(worldName, LevelStem.OVERWORLD);
MinecraftServer.convertFromRegionFormatIfNeeded(convertable_conversionsession);
LevelSummary worldinfo = convertable_conversionsession.getSummary();
@@ -158,7 +149,6 @@ public class Main {
return;
}
- DataPackConfig datapackconfiguration = convertable_conversionsession.getDataPacks();
boolean flag = optionset.has("safeMode");
if (flag) {
@@ -166,24 +156,24 @@ public class Main {
}
PackRepository resourcepackrepository = new PackRepository(PackType.SERVER_DATA, new RepositorySource[]{new ServerPacksSource(), new FolderRepositorySource(convertable_conversionsession.getLevelPath(LevelResource.DATAPACK_DIR).toFile(), PackSource.WORLD)});
- // CraftBukkit start
- File bukkitDataPackFolder = new File(convertable_conversionsession.getLevelPath(LevelResource.DATAPACK_DIR).toFile(), "bukkit");
- if (!bukkitDataPackFolder.exists()) {
- bukkitDataPackFolder.mkdirs();
- }
- File mcMeta = new File(bukkitDataPackFolder, "pack.mcmeta");
- try {
- com.google.common.io.Files.write("{\n"
- + " \"pack\": {\n"
- + " \"description\": \"Data pack for resources provided by Bukkit plugins\",\n"
- + " \"pack_format\": " + SharedConstants.getCurrentVersion().getPackVersion() + "\n"
- + " }\n"
- + "}\n", mcMeta, com.google.common.base.Charsets.UTF_8);
- } catch (java.io.IOException ex) {
- throw new RuntimeException("Could not initialize Bukkit datapack", ex);
- }
+// // CraftBukkit start
+// File bukkitDataPackFolder = new File(convertable_conversionsession.getLevelPath(LevelResource.DATAPACK_DIR).toFile(), "bukkit");
+// if (!bukkitDataPackFolder.exists()) {
+// bukkitDataPackFolder.mkdirs();
+// }
+// File mcMeta = new File(bukkitDataPackFolder, "pack.mcmeta");
+// try {
+// com.google.common.io.Files.write("{\n"
+// + " \"pack\": {\n"
+// + " \"description\": \"Data pack for resources provided by Bukkit plugins\",\n"
+// + " \"pack_format\": " + SharedConstants.getCurrentVersion().getPackVersion() + "\n"
+// + " }\n"
+// + "}\n", mcMeta, com.google.common.base.Charsets.UTF_8);
+// } catch (java.io.IOException ex) {
+// throw new RuntimeException("Could not initialize Bukkit datapack", ex);
+// }
// CraftBukkit end
- DataPackConfig datapackconfiguration1 = MinecraftServer.configurePackRepository(resourcepackrepository, datapackconfiguration == null ? DataPackConfig.DEFAULT : datapackconfiguration, flag);
+ DataPackConfig datapackconfiguration1 = MinecraftServer.configurePackRepository(resourcepackrepository, DataPackConfig.DEFAULT, flag);
CompletableFuture completablefuture = ServerResources.loadResources(resourcepackrepository.openAllSelected(), iregistrycustom_dimension, Commands.CommandSelection.DEDICATED, dedicatedserversettings.getProperties().functionPermissionLevel, Util.backgroundExecutor(), Runnable::run);
ServerResources datapackresources;
@@ -292,38 +282,4 @@ public class Main {
worldUpgrader.convert();
}
// Paper end - fix and optimise world upgrading
-
- public static void forceUpgrade(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, boolean eraseCache, BooleanSupplier booleansupplier, ImmutableSet<ResourceKey<DimensionType>> worlds) { // CraftBukkit
- Main.LOGGER.info("Forcing world upgrade! {}", session.getLevelId()); // CraftBukkit
- WorldUpgrader worldupgrader = new WorldUpgrader(session, dataFixer, worlds, eraseCache);
- Component ichatbasecomponent = null;
-
- while (!worldupgrader.isFinished()) {
- Component ichatbasecomponent1 = worldupgrader.getStatus();
-
- if (ichatbasecomponent != ichatbasecomponent1) {
- ichatbasecomponent = ichatbasecomponent1;
- Main.LOGGER.info(worldupgrader.getStatus().getString());
- }
-
- int i = worldupgrader.getTotalChunks();
-
- if (i > 0) {
- int j = worldupgrader.getConverted() + worldupgrader.getSkipped();
-
- Main.LOGGER.info("{}% completed ({} / {} chunks)...", Mth.floor((float) j / (float) i * 100.0F), j, i);
- }
-
- if (!booleansupplier.getAsBoolean()) {
- worldupgrader.cancel();
- } else {
- try {
- Thread.sleep(1000L);
- } catch (InterruptedException interruptedexception) {
- ;
- }
- }
- }
-
- }
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index fe6d5051b139cd6079e288ffdf20e30fdd46fdda..070aa091094e0fb61533471b7e0650f12afd271c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -12,6 +12,7 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.datafixers.DataFixer;
+import dev.cobblesword.skypaper.SkyPaper;
import io.papermc.paper.adventure.PaperAdventure; // Paper
import it.unimi.dsi.fastutil.longs.LongIterator;
import java.awt.GraphicsEnvironment;
@@ -1209,6 +1210,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected void runServer() {
try {
long serverStartTime = Util.getNanos(); // Paper
+
if (this.initServer()) {
this.nextTickTime = Util.getMillis();
this.status.setDescription(new TextComponent(this.motd));
@@ -1229,6 +1231,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
org.spigotmc.WatchdogThread.tick(); // Paper
org.spigotmc.WatchdogThread.hasStarted = true; // Paper
+
Arrays.fill( recentTps, 20 );
long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
lastTick = start - TICK_TIME; // Paper
@@ -1421,10 +1424,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private void updateStatusIcon(ServerStatus metadata) {
Optional<File> optional = Optional.of(this.getFile("server-icon.png")).filter(File::isFile);
- if (!optional.isPresent()) {
- optional = this.storageSource.getIconFile().map(Path::toFile).filter(File::isFile);
- }
-
optional.ifPresent((file) -> {
try {
BufferedImage bufferedimage = ImageIO.read(file);
@@ -1445,10 +1444,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
});
}
- public Optional<Path> getWorldScreenshotFile() {
- return this.storageSource.getIconFile();
- }
-
public File getServerDirectory() {
return new File(".");
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index b3c99c1678c3ee159861c8aac38e765d664c4d1d..ff036a08755db90c186831c0bf057e47cf6b9438 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -393,7 +393,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
- super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync);
+ super(world.databaseId, new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync);
// Paper - don't copy
this.pendingUnloads = new Long2ObjectLinkedOpenHashMap();
this.entitiesInLevel = new LongOpenHashSet();
@@ -435,7 +435,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), threadedmailbox1, this.queueSorter.getProcessor(threadedmailbox1, false));
this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor); this.distanceManager.chunkMap = this; // Paper
this.overworldDataStorage = persistentStateManagerFactory;
- this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world);
+ this.poiManager = new PoiManager(level.databaseId, new File(file, "poi"), dataFixer, dsync, world);
this.setViewDistance(viewDistance);
// Paper start
this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 7470f3ba66c2e894b5a5b0ba392ecabf8b04aff9..66049c578a45bf4959430cc64edef5aafba98214 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -397,10 +397,9 @@ public class ServerChunkCache extends ChunkSource {
this.generator = chunkGenerator;
this.mainThread = Thread.currentThread();
File file = session.getDimensionPath(world.dimension());
- File file1 = new File(file, "data");
- file1.mkdirs();
- this.dataStorage = new DimensionDataStorage(file1, dataFixer);
+ File file1 = new File(file, "data");
+ this.dataStorage = new DimensionDataStorage(world.databaseId, file1, dataFixer);
this.chunkMap = new ChunkMap(world, session, dataFixer, structureManager, workerExecutor, this.mainThreadProcessor, this, this.getGenerator(), worldGenerationProgressListener, chunkstatusupdatelistener, supplier, viewDistance, flag);
this.lightEngine = this.chunkMap.getLightEngine();
this.distanceManager = this.chunkMap.getDistanceManager();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 9e4ad810dd6348ad95c9a7e6d1bd63f6ec37c986..9da281988eb8d7a3b8fd5346b16182e3a9b908b1 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -4,6 +4,7 @@ import com.google.common.annotations.VisibleForTesting;
import co.aikar.timings.TimingHistory; // Paper
import com.google.common.collect.Lists;
import com.mojang.datafixers.DataFixer;
+import dev.cobblesword.skypaper.database.dto.WorldDTO;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
@@ -103,7 +104,6 @@ import net.minecraft.world.level.ExplosionDamageCalculator;
import net.minecraft.world.level.ForcedChunksSavedData;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
-import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.NaturalSpawner;
import net.minecraft.world.level.ServerTickList;
import net.minecraft.world.level.StructureFeatureManager;
@@ -168,6 +168,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0);
private static final Logger LOGGER = LogManager.getLogger();
private static final int EMPTY_TIME_NO_TICK = 300;
+
+ public int databaseId = -1;
+
public final List<ServerPlayer> players;
public final ServerChunkCache chunkSource;
private final MinecraftServer server;
@@ -476,7 +479,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, biomeProvider, env, executor); // Paper - Anti-Xray - Pass executor
this.pvpMode = minecraftserver.isPvpAllowed();
this.convertable = convertable_conversionsession;
- this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelPath.toFile());
+ WorldDTO worldDTO = WorldUUID.getUUID(convertable_conversionsession.levelPath.toFile());
+ this.databaseId = worldDTO.databaseId;
+ this.uuid = worldDTO.uuid;
// CraftBukkit end
this.players = Lists.newArrayList();
this.entityTickList = new EntityTickList();
diff --git a/src/main/java/net/minecraft/server/packs/repository/FolderRepositorySource.java b/src/main/java/net/minecraft/server/packs/repository/FolderRepositorySource.java
new file mode 100644
index 0000000000000000000000000000000000000000..96e27fcf5f38882de4f3194afe7329072e94aaf3
--- /dev/null
+++ b/src/main/java/net/minecraft/server/packs/repository/FolderRepositorySource.java
@@ -0,0 +1,55 @@
+package net.minecraft.server.packs.repository;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import net.minecraft.server.packs.FilePackResources;
+import net.minecraft.server.packs.FolderPackResources;
+import net.minecraft.server.packs.PackResources;
+
+public class FolderRepositorySource implements RepositorySource {
+ private static final FileFilter RESOURCEPACK_FILTER = (file) -> {
+ boolean bl = file.isFile() && file.getName().endsWith(".zip");
+ boolean bl2 = file.isDirectory() && (new File(file, "pack.mcmeta")).isFile();
+ return bl || bl2;
+ };
+ private final File folder;
+ private final PackSource packSource;
+
+ public FolderRepositorySource(File packsFolder, PackSource source) {
+ this.folder = packsFolder;
+ this.packSource = source;
+ }
+
+ @Override
+ public void loadPacks(Consumer<Pack> profileAdder, Pack.PackConstructor factory) {
+ if(SkyPaper.isDataPacksDisabled()) return;
+
+ if (!this.folder.isDirectory()) {
+ this.folder.mkdirs();
+ }
+
+ File[] files = this.folder.listFiles(RESOURCEPACK_FILTER);
+ if (files != null) {
+ for(File file : files) {
+ String string = "file/" + file.getName();
+ Pack pack = Pack.create(string, false, this.createSupplier(file), factory, Pack.Position.TOP, this.packSource);
+ if (pack != null) {
+ profileAdder.accept(pack);
+ }
+ }
+
+ }
+ }
+
+ private Supplier<PackResources> createSupplier(File file) {
+ return file.isDirectory() ? () -> {
+ return new FolderPackResources(file);
+ } : () -> {
+ return new FilePackResources(file);
+ };
+ }
+}
diff --git a/src/main/java/net/minecraft/server/packs/repository/ServerPacksSource.java b/src/main/java/net/minecraft/server/packs/repository/ServerPacksSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..84b12b7cc6d9382be85279ddf38b2b167c25be21
--- /dev/null
+++ b/src/main/java/net/minecraft/server/packs/repository/ServerPacksSource.java
@@ -0,0 +1,27 @@
+package net.minecraft.server.packs.repository;
+
+import java.util.function.Consumer;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import net.minecraft.SharedConstants;
+import net.minecraft.network.chat.TranslatableComponent;
+import net.minecraft.server.packs.PackType;
+import net.minecraft.server.packs.VanillaPackResources;
+import net.minecraft.server.packs.metadata.pack.PackMetadataSection;
+
+public class ServerPacksSource implements RepositorySource {
+ public static final PackMetadataSection BUILT_IN_METADATA = new PackMetadataSection(new TranslatableComponent("dataPack.vanilla.description"), PackType.SERVER_DATA.getVersion(SharedConstants.getCurrentVersion()));
+ public static final String VANILLA_ID = "vanilla";
+ private final VanillaPackResources vanillaPack = new VanillaPackResources(BUILT_IN_METADATA, "minecraft");
+
+ @Override
+ public void loadPacks(Consumer<Pack> profileAdder, Pack.PackConstructor factory) {
+ Pack pack = Pack.create("vanilla", false, () -> {
+ return this.vanillaPack;
+ }, factory, Pack.Position.BOTTOM, PackSource.BUILT_IN);
+ if (pack != null) {
+ profileAdder.accept(pack);
+ }
+
+ }
+}
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
index b006519f2aacc84b45ab25e46d7b9112f9de816e..6291b5c58aa65b7cec4369b489c8b563ff731c67 100644
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
@@ -1,299 +1,7 @@
package net.minecraft.util.worldupdate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.UnmodifiableIterator;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.mojang.datafixers.DataFixer;
-import it.unimi.dsi.fastutil.objects.Object2FloatMap;
-import it.unimi.dsi.fastutil.objects.Object2FloatMaps;
-import it.unimi.dsi.fastutil.objects.Object2FloatOpenCustomHashMap;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.concurrent.ThreadFactory;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import net.minecraft.ReportedException;
-import net.minecraft.SharedConstants;
-import net.minecraft.Util;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.network.chat.Component;
-import net.minecraft.network.chat.TranslatableComponent;
-import net.minecraft.resources.ResourceKey;
-import net.minecraft.world.level.ChunkPos;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.chunk.storage.ChunkStorage;
-import net.minecraft.world.level.chunk.storage.RegionFile;
-import net.minecraft.world.level.dimension.DimensionType;
-import net.minecraft.world.level.storage.DimensionDataStorage;
-import net.minecraft.world.level.storage.LevelStorageSource;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
public class WorldUpgrader {
-
- private static final Logger LOGGER = LogManager.getLogger();
- private static final ThreadFactory THREAD_FACTORY = (new ThreadFactoryBuilder()).setDaemon(true).build();
- private final ImmutableSet<ResourceKey<DimensionType>> levels; // CraftBukkit
- private final boolean eraseCache;
- private final LevelStorageSource.LevelStorageAccess levelStorage;
- private final Thread thread;
- private final DataFixer dataFixer;
- private volatile boolean running = true;
- private volatile boolean finished;
- private volatile float progress;
- private volatile int totalChunks;
- private volatile int converted;
- private volatile int skipped;
- private final Object2FloatMap<ResourceKey<DimensionType>> progressMap = Object2FloatMaps.synchronize(new Object2FloatOpenCustomHashMap(Util.identityStrategy())); // CraftBukkit
- private volatile Component status = new TranslatableComponent("optimizeWorld.stage.counting");
public static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
- private final DimensionDataStorage overworldDataStorage;
-
- public WorldUpgrader(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, ImmutableSet<ResourceKey<DimensionType>> worlds, boolean eraseCache) { // CraftBukkit
- this.levels = worlds;
- this.eraseCache = eraseCache;
- this.dataFixer = dataFixer;
- this.levelStorage = session;
- this.overworldDataStorage = new DimensionDataStorage(new File(this.levelStorage.getDimensionPath(Level.OVERWORLD), "data"), dataFixer);
- this.thread = WorldUpgrader.THREAD_FACTORY.newThread(this::work);
- this.thread.setUncaughtExceptionHandler((thread, throwable) -> {
- WorldUpgrader.LOGGER.error("Error upgrading world", throwable);
- this.status = new TranslatableComponent("optimizeWorld.stage.failed");
- this.finished = true;
- });
- this.thread.start();
- }
-
- public void cancel() {
- this.running = false;
-
- try {
- this.thread.join();
- } catch (InterruptedException interruptedexception) {
- ;
- }
-
- }
-
- private void work() {
- this.totalChunks = 0;
- Builder<ResourceKey<DimensionType>, ListIterator<ChunkPos>> builder = ImmutableMap.builder(); // CraftBukkit
-
- List list;
-
- for (UnmodifiableIterator unmodifiableiterator = this.levels.iterator(); unmodifiableiterator.hasNext(); this.totalChunks += list.size()) {
- ResourceKey<DimensionType> resourcekey = (ResourceKey) unmodifiableiterator.next(); // CraftBukkit
-
- list = this.getAllChunkPos(resourcekey);
- builder.put(resourcekey, list.listIterator());
- }
-
- if (this.totalChunks == 0) {
- this.finished = true;
- } else {
- float f = (float) this.totalChunks;
- ImmutableMap<ResourceKey<DimensionType>, ListIterator<ChunkPos>> immutablemap = builder.build(); // CraftBukkit
- Builder<ResourceKey<DimensionType>, ChunkStorage> builder1 = ImmutableMap.builder(); // CraftBukkit
- UnmodifiableIterator unmodifiableiterator1 = this.levels.iterator();
-
- while (unmodifiableiterator1.hasNext()) {
- ResourceKey<DimensionType> resourcekey1 = (ResourceKey) unmodifiableiterator1.next(); // CraftBukkit
- File file = this.levelStorage.getDimensionPath((ResourceKey) null); // CraftBukkit
-
- builder1.put(resourcekey1, new ChunkStorage(new File(file, "region"), this.dataFixer, true));
- }
-
- ImmutableMap<ResourceKey<DimensionType>, ChunkStorage> immutablemap1 = builder1.build(); // CraftBukkit
- long i = Util.getMillis();
-
- this.status = new TranslatableComponent("optimizeWorld.stage.upgrading");
-
- while (this.running) {
- boolean flag = false;
- float f1 = 0.0F;
-
- float f2;
-
- for (UnmodifiableIterator unmodifiableiterator2 = this.levels.iterator(); unmodifiableiterator2.hasNext(); f1 += f2) {
- ResourceKey<DimensionType> resourcekey2 = (ResourceKey) unmodifiableiterator2.next(); // CraftBukkit
- ListIterator<ChunkPos> listiterator = (ListIterator) immutablemap.get(resourcekey2);
- ChunkStorage ichunkloader = (ChunkStorage) immutablemap1.get(resourcekey2);
-
- if (listiterator.hasNext()) {
- ChunkPos chunkcoordintpair = (ChunkPos) listiterator.next();
- boolean flag1 = false;
-
- try {
- CompoundTag nbttagcompound = ichunkloader.read(chunkcoordintpair);
-
- if (nbttagcompound != null) {
- int j = ChunkStorage.getVersion(nbttagcompound);
- CompoundTag nbttagcompound1 = ichunkloader.getChunkData(resourcekey2, () -> {
- return this.overworldDataStorage;
- }, nbttagcompound, chunkcoordintpair, null); // CraftBukkit
- CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("Level");
- ChunkPos chunkcoordintpair1 = new ChunkPos(nbttagcompound2.getInt("xPos"), nbttagcompound2.getInt("zPos"));
-
- if (!chunkcoordintpair1.equals(chunkcoordintpair)) {
- WorldUpgrader.LOGGER.warn("Chunk {} has invalid position {}", chunkcoordintpair, chunkcoordintpair1);
- }
-
- boolean flag2 = j < SharedConstants.getCurrentVersion().getWorldVersion();
-
- if (this.eraseCache) {
- flag2 = flag2 || nbttagcompound2.contains("Heightmaps");
- nbttagcompound2.remove("Heightmaps");
- flag2 = flag2 || nbttagcompound2.contains("isLightOn");
- nbttagcompound2.remove("isLightOn");
- }
-
- if (flag2) {
- ichunkloader.write(chunkcoordintpair, nbttagcompound1);
- flag1 = true;
- }
- }
- } catch (ReportedException reportedexception) {
- Throwable throwable = reportedexception.getCause();
-
- if (!(throwable instanceof IOException)) {
- throw reportedexception;
- }
-
- WorldUpgrader.LOGGER.error("Error upgrading chunk {}", chunkcoordintpair, throwable);
- } catch (IOException ioexception) {
- WorldUpgrader.LOGGER.error("Error upgrading chunk {}", chunkcoordintpair, ioexception);
- }
-
- if (flag1) {
- ++this.converted;
- } else {
- ++this.skipped;
- }
-
- flag = true;
- }
-
- f2 = (float) listiterator.nextIndex() / f;
- this.progressMap.put(resourcekey2, f2);
- }
-
- this.progress = f1;
- if (!flag) {
- this.running = false;
- }
- }
-
- this.status = new TranslatableComponent("optimizeWorld.stage.finished");
- UnmodifiableIterator unmodifiableiterator3 = immutablemap1.values().iterator();
-
- while (unmodifiableiterator3.hasNext()) {
- ChunkStorage ichunkloader1 = (ChunkStorage) unmodifiableiterator3.next();
-
- try {
- ichunkloader1.close();
- } catch (IOException ioexception1) {
- WorldUpgrader.LOGGER.error("Error upgrading chunk", ioexception1);
- }
- }
-
- this.overworldDataStorage.save();
- i = Util.getMillis() - i;
- WorldUpgrader.LOGGER.info("World optimizaton finished after {} ms", i);
- this.finished = true;
- }
- }
-
- private List<ChunkPos> getAllChunkPos(ResourceKey<DimensionType> world) { // CraftBukkit
- File file = this.levelStorage.getDimensionPath((ResourceKey) null); // CraftBukkit
- File file1 = new File(file, "region");
- File[] afile = file1.listFiles((file2, s) -> {
- return s.endsWith(".mca");
- });
-
- if (afile == null) {
- return ImmutableList.of();
- } else {
- List<ChunkPos> list = Lists.newArrayList();
- File[] afile1 = afile;
- int i = afile.length;
-
- for (int j = 0; j < i; ++j) {
- File file2 = afile1[j];
- Matcher matcher = WorldUpgrader.REGEX.matcher(file2.getName());
-
- if (matcher.matches()) {
- int k = Integer.parseInt(matcher.group(1)) << 5;
- int l = Integer.parseInt(matcher.group(2)) << 5;
-
- try {
- RegionFile regionfile = new RegionFile(file2, file1, true);
-
- try {
- for (int i1 = 0; i1 < 32; ++i1) {
- for (int j1 = 0; j1 < 32; ++j1) {
- ChunkPos chunkcoordintpair = new ChunkPos(i1 + k, j1 + l);
-
- if (regionfile.doesChunkExist(chunkcoordintpair)) {
- list.add(chunkcoordintpair);
- }
- }
- }
- } catch (Throwable throwable) {
- try {
- regionfile.close();
- } catch (Throwable throwable1) {
- throwable.addSuppressed(throwable1);
- }
-
- throw throwable;
- }
-
- regionfile.close();
- } catch (Throwable throwable2) {
- ;
- }
- }
- }
-
- return list;
- }
- }
-
- public boolean isFinished() {
- return this.finished;
- }
-
- public ImmutableSet<ResourceKey<Level>> levels() {
- throw new AssertionError("Unsupported"); // CraftBukkit
- }
-
- public float dimensionProgress(ResourceKey<Level> world) {
- return this.progressMap.getFloat(world);
- }
-
- public float getProgress() {
- return this.progress;
- }
-
- public int getTotalChunks() {
- return this.totalChunks;
- }
-
- public int getConverted() {
- return this.converted;
- }
-
- public int getSkipped() {
- return this.skipped;
- }
-
- public Component getStatus() {
- return this.status;
- }
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 2b79ace854461b216dc4970d1cc4a3953a51dd50..6bef76f87fcd14ba88d067d0bde93d84c133f474 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -39,8 +39,8 @@ public class PoiManager extends SectionStorage<PoiSection> {
private final LongSet loadedChunks = new LongOpenHashSet();
public final net.minecraft.server.level.ServerLevel world; // Paper // Paper public
- public PoiManager(File directory, DataFixer dataFixer, boolean dsync, LevelHeightAccessor world) {
- super(directory, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, world);
+ public PoiManager(int worldDatabaseId, File directory, DataFixer dataFixer, boolean dsync, LevelHeightAccessor world) {
+ super(worldDatabaseId, directory, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, world);
this.world = (net.minecraft.server.level.ServerLevel)world; // Paper
this.distanceTracker = new PoiManager.DistanceTracker();
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
index 747e2a31f055d84d4321c8241e1b6c2918557e91..5bf8ae8369c26c56b1dfc253a9e5e2e6c098bcd2 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -30,11 +30,11 @@ public class ChunkStorage implements AutoCloseable {
public final RegionFileStorage regionFileCache;
// Paper end - async chunk loading
- public ChunkStorage(File directory, DataFixer dataFixer, boolean dsync) {
+ public ChunkStorage(int worldDatabaseId, File directory, DataFixer dataFixer, boolean dsync) {
this.fixerUpper = dataFixer;
// Paper start - async chunk io
// remove IO worker
- this.regionFileCache = new RegionFileStorage(directory, dsync, true); // Paper - nuke IOWorker // Paper
+ this.regionFileCache = new RegionFileStorage(worldDatabaseId, directory, dsync, true); // Paper - nuke IOWorker // Paper
// Paper end - async chunk io
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
index 916f93b097a65f95e830fe5e1567c85d304f808f..60278d391440f03c82f53a3d726c6be50a8a7d12 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
@@ -43,7 +43,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
this.level = world;
this.fixerUpper = dataFixer;
this.entityDeserializerQueue = ProcessorMailbox.create(executor, "entity-deserializer");
- this.worker = new IOWorker(chunkFile, dsync, "entities");
+ this.worker = new IOWorker(this.level.databaseId, chunkFile, dsync, "entities");
}
@Override
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/IOWorker.java b/src/main/java/net/minecraft/world/level/chunk/storage/IOWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f0e76521e93a3663f6f293f300c3dafe86243a6
--- /dev/null
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/IOWorker.java
@@ -0,0 +1,175 @@
+package net.minecraft.world.level.chunk.storage;
+
+import com.google.common.collect.Maps;
+import com.mojang.datafixers.util.Either;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+import net.minecraft.Util;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.util.Unit;
+import net.minecraft.util.thread.ProcessorMailbox;
+import net.minecraft.util.thread.StrictQueue;
+import net.minecraft.world.level.ChunkPos;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class IOWorker implements AutoCloseable {
+ private static final Logger LOGGER = LogManager.getLogger();
+ private final AtomicBoolean shutdownRequested = new AtomicBoolean();
+ private final ProcessorMailbox<StrictQueue.IntRunnable> mailbox;
+ private final RegionFileStorage storage;
+ private final Map<ChunkPos, IOWorker.PendingStore> pendingWrites = Maps.newLinkedHashMap();
+
+ protected IOWorker(int worldDatabaseId, File directory, boolean dsync, String name) {
+ this.storage = new RegionFileStorage(worldDatabaseId, directory, dsync);
+ this.mailbox = new ProcessorMailbox<>(new StrictQueue.FixedPriorityQueue(IOWorker.Priority.values().length), Util.ioPool(), "IOWorker-" + name);
+ }
+
+ public CompletableFuture<Void> store(ChunkPos pos, @Nullable CompoundTag nbt) {
+ return this.submitTask(() -> {
+ IOWorker.PendingStore pendingStore = this.pendingWrites.computeIfAbsent(pos, (chunkPos) -> {
+ return new IOWorker.PendingStore(nbt);
+ });
+ pendingStore.data = nbt;
+ return Either.left(pendingStore.result);
+ }).thenCompose(Function.identity());
+ }
+
+ @Nullable
+ public CompoundTag load(ChunkPos pos) throws IOException {
+ CompletableFuture<CompoundTag> completableFuture = this.loadAsync(pos);
+
+ try {
+ return completableFuture.join();
+ } catch (CompletionException var4) {
+ if (var4.getCause() instanceof IOException) {
+ throw (IOException)var4.getCause();
+ } else {
+ throw var4;
+ }
+ }
+ }
+
+ protected CompletableFuture<CompoundTag> loadAsync(ChunkPos pos) {
+ return this.submitTask(() -> {
+ IOWorker.PendingStore pendingStore = this.pendingWrites.get(pos);
+ if (pendingStore != null) {
+ return Either.left(pendingStore.data);
+ } else {
+ try {
+ CompoundTag compoundTag = this.storage.read(pos);
+ return Either.left(compoundTag);
+ } catch (Exception var4) {
+ LOGGER.warn("Failed to read chunk {}", pos, var4);
+ return Either.right(var4);
+ }
+ }
+ });
+ }
+
+ public CompletableFuture<Void> synchronize(boolean bl) {
+ CompletableFuture<Void> completableFuture = this.submitTask(() -> {
+ return Either.left(CompletableFuture.allOf(this.pendingWrites.values().stream().map((pendingStore) -> {
+ return pendingStore.result;
+ }).toArray((i) -> {
+ return new CompletableFuture[i];
+ })));
+ }).thenCompose(Function.identity());
+ return bl ? completableFuture.thenCompose((void_) -> {
+ return this.submitTask(() -> {
+ try {
+ this.storage.flush();
+ return Either.left((Void)null);
+ } catch (Exception var2) {
+ LOGGER.warn("Failed to synchronize chunks", (Throwable)var2);
+ return Either.right(var2);
+ }
+ });
+ }) : completableFuture.thenCompose((void_) -> {
+ return this.submitTask(() -> {
+ return Either.left((Void)null);
+ });
+ });
+ }
+
+ private <T> CompletableFuture<T> submitTask(Supplier<Either<T, Exception>> task) {
+ return this.mailbox.askEither((processorHandle) -> {
+ return new StrictQueue.IntRunnable(IOWorker.Priority.FOREGROUND.ordinal(), () -> {
+ if (!this.shutdownRequested.get()) {
+ processorHandle.tell(task.get());
+ }
+
+ this.tellStorePending();
+ });
+ });
+ }
+
+ private void storePendingChunk() {
+ if (!this.pendingWrites.isEmpty()) {
+ Iterator<Entry<ChunkPos, IOWorker.PendingStore>> iterator = this.pendingWrites.entrySet().iterator();
+ Entry<ChunkPos, IOWorker.PendingStore> entry = iterator.next();
+ iterator.remove();
+ this.runStore(entry.getKey(), entry.getValue());
+ this.tellStorePending();
+ }
+ }
+
+ private void tellStorePending() {
+ this.mailbox.tell(new StrictQueue.IntRunnable(IOWorker.Priority.BACKGROUND.ordinal(), this::storePendingChunk));
+ }
+
+ private void runStore(ChunkPos pos, IOWorker.PendingStore result) {
+ try {
+ this.storage.write(pos, result.data);
+ result.result.complete((Void)null);
+ } catch (Exception var4) {
+ LOGGER.error("Failed to store chunk {}", pos, var4);
+ result.result.completeExceptionally(var4);
+ }
+
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (this.shutdownRequested.compareAndSet(false, true)) {
+ this.mailbox.ask((processorHandle) -> {
+ return new StrictQueue.IntRunnable(IOWorker.Priority.SHUTDOWN.ordinal(), () -> {
+ processorHandle.tell(Unit.INSTANCE);
+ });
+ }).join();
+ this.mailbox.close();
+
+ try {
+ this.storage.close();
+ } catch (Exception var2) {
+ LOGGER.error("Failed to close storage", (Throwable)var2);
+ }
+
+ }
+ }
+
+ static class PendingStore {
+ @Nullable
+ CompoundTag data;
+ final CompletableFuture<Void> result = new CompletableFuture<>();
+
+ public PendingStore(@Nullable CompoundTag nbt) {
+ this.data = nbt;
+ }
+ }
+
+ static enum Priority {
+ FOREGROUND,
+ BACKGROUND,
+ SHUTDOWN;
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 4e0006b73fbd2634aa42334ae9dde79b4eccaf38..4ea94d5c661fc16b1012090e734a8dba63ba8f9a 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -1,5 +1,6 @@
package net.minecraft.world.level.chunk.storage;
+import dev.cobblesword.skypaper.SkyPaper;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.io.DataInput;
@@ -24,18 +25,20 @@ public class RegionFileStorage implements AutoCloseable {
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap();
private final File folder;
private final boolean sync;
+ private int worldDatabaseId;
private final boolean isChunkData; // Paper
- RegionFileStorage(File directory, boolean dsync) {
+ RegionFileStorage(int worldDatabaseId, File directory, boolean dsync) {
// Paper start - add isChunkData param
- this(directory, dsync, false);
+ this(worldDatabaseId, directory, dsync, false);
}
- RegionFileStorage(File directory, boolean dsync, boolean isChunkData) {
+ RegionFileStorage(int worldDatabaseId, File directory, boolean dsync, boolean isChunkData) {
this.isChunkData = isChunkData;
// Paper end - add isChunkData param
this.folder = directory;
this.sync = dsync;
+ this.worldDatabaseId = worldDatabaseId;
}
// Paper start
@@ -182,14 +185,17 @@ public class RegionFileStorage implements AutoCloseable {
// Paper End
@Nullable
- public CompoundTag read(ChunkPos pos) throws IOException {
- // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
- RegionFile regionfile = this.getFile(pos, true, true); // Paper
- if (regionfile == null) {
- return null;
- }
- // Paper start - Add regionfile parameter
- return this.read(pos, regionfile);
+ public CompoundTag read(ChunkPos pos) throws IOException
+ {
+ String dataType = this.folder.getName();
+ return SkyPaper.INSTANCE.getWorldChunkDao().loadWorldChunk(dataType, this.worldDatabaseId, pos);
+// // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
+// RegionFile regionfile = this.getFile(pos, true, true); // Paper
+// if (regionfile == null) {
+// return null;
+// }
+// // Paper start - Add regionfile parameter
+// return this.read(pos, regionfile);
}
public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException {
// We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile
@@ -256,51 +262,55 @@ public class RegionFileStorage implements AutoCloseable {
}
protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
- RegionFile regionfile = this.getFile(pos, false, true); // CraftBukkit // Paper
- try { // Paper
- int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
-
- if (nbt == null) {
- regionfile.clear(pos);
- } else {
- DataOutputStream dataoutputstream = regionfile.getChunkDataOutputStream(pos);
-
- try {
- NbtIo.write(nbt, (DataOutput) dataoutputstream);
- regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
- regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
- } catch (Throwable throwable) {
- if (dataoutputstream != null) {
- try {
- dataoutputstream.close();
- } catch (Throwable throwable1) {
- throwable.addSuppressed(throwable1);
- }
- }
-
- throw throwable;
- }
-
- if (dataoutputstream != null) {
- dataoutputstream.close();
- }
- }
-
- // Paper start
- return;
- } catch (Exception ex) {
- laste = ex;
- }
- }
-
- if (laste != null) {
- com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste);
- MinecraftServer.LOGGER.error("Failed to save chunk", laste);
- }
- // Paper end
- } finally { // Paper start
- regionfile.fileLock.unlock();
- } // Paper end
+ String dataType = this.folder.getName();
+ SkyPaper.INSTANCE.getWorldChunkDao().saveWorldChunk(dataType, this.worldDatabaseId, pos, nbt);
+
+//
+// RegionFile regionfile = this.getFile(pos, false, true); // CraftBukkit // Paper
+// try { // Paper
+// int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
+//
+// if (nbt == null) {
+// regionfile.clear(pos);
+// } else {
+// DataOutputStream dataoutputstream = regionfile.getChunkDataOutputStream(pos);
+//
+// try {
+// NbtIo.write(nbt, (DataOutput) dataoutputstream);
+// regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
+// regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
+// } catch (Throwable throwable) {
+// if (dataoutputstream != null) {
+// try {
+// dataoutputstream.close();
+// } catch (Throwable throwable1) {
+// throwable.addSuppressed(throwable1);
+// }
+// }
+//
+// throw throwable;
+// }
+//
+// if (dataoutputstream != null) {
+// dataoutputstream.close();
+// }
+// }
+//
+// // Paper start
+// return;
+// } catch (Exception ex) {
+// laste = ex;
+// }
+// }
+//
+// if (laste != null) {
+// com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste);
+// MinecraftServer.LOGGER.error("Failed to save chunk", laste);
+// }
+// // Paper end
+// } finally { // Paper start
+// regionfile.fileLock.unlock();
+// } // Paper end
}
public synchronized void close() throws IOException { // Paper -> synchronized
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
index a7a51aa31628d9dbc30f43ef74022b0cd917be7c..bd02fe9559cf0dafa3ef57eb222f77153620e317 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -42,8 +42,8 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
private final DataFixTypes type;
protected final LevelHeightAccessor levelHeightAccessor;
- public SectionStorage(File directory, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, LevelHeightAccessor world) {
- super(directory, dsync); // Paper - nuke IOWorker
+ public SectionStorage(int worldDatabaseId, File directory, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, LevelHeightAccessor world) {
+ super(worldDatabaseId, directory, dsync); // Paper - nuke IOWorker
this.codec = codecFactory;
this.factory = factory;
this.fixerUpper = dataFixer;
diff --git a/src/main/java/net/minecraft/world/level/saveddata/SavedData.java b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c14bdcd57aa8fd4b6b1520761d2bbe977d5dfd1
--- /dev/null
+++ b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
@@ -0,0 +1,42 @@
+package net.minecraft.world.level.saveddata;
+
+import java.io.File;
+import java.io.IOException;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import net.minecraft.SharedConstants;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtIo;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public abstract class SavedData {
+ private static final Logger LOGGER = LogManager.getLogger();
+ private boolean dirty;
+
+ public abstract CompoundTag save(CompoundTag nbt);
+
+ public void setDirty() {
+ this.setDirty(true);
+ }
+
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+ public boolean isDirty() {
+ return this.dirty;
+ }
+
+ public void save(int worldDatabaseId, File file) {
+ if (this.isDirty()) {
+ CompoundTag compoundTag = new CompoundTag();
+ compoundTag.put("data", this.save(new CompoundTag()));
+ compoundTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
+
+ SkyPaper.INSTANCE.getWorldDataCacheDao().saveWorldDataCache(worldDatabaseId, file.getName(), compoundTag);
+
+ this.setDirty(false);
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 60d7496966b22e0553372a93e3c0e7ed9e166cba..5c7c167f80edc1f30e344456c9658b6f99718d39 100644
--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -42,7 +42,6 @@ import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.map.CraftMapView;
-import org.bukkit.craftbukkit.util.CraftChatMessage;
// CraftBukkit end
public class MapItemSavedData extends SavedData {
diff --git a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
index c8ed0673ff819cb88d0ee6f53f2a2b9b46b203d4..75126ea1f387cf65dd0e7fe639b178676dae279e 100644
--- a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
@@ -2,20 +2,17 @@ package net.minecraft.world.level.storage;
import com.google.common.collect.Maps;
import com.mojang.datafixers.DataFixer;
-import java.io.DataInputStream;
+
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.PushbackInputStream;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
+
+import dev.cobblesword.skypaper.SkyPaper;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.NbtIo;
-import net.minecraft.nbt.NbtUtils;
-import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.saveddata.SavedData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -25,8 +22,10 @@ public class DimensionDataStorage {
public final Map<String, SavedData> cache = Maps.newHashMap();
private final DataFixer fixerUpper;
private final File dataFolder;
+ private int worldDatabaseId;
- public DimensionDataStorage(File directory, DataFixer dataFixer) {
+ public DimensionDataStorage(int databaseId, File directory, DataFixer dataFixer) {
+ this.worldDatabaseId = databaseId;
this.fixerUpper = dataFixer;
this.dataFolder = directory;
}
@@ -60,11 +59,12 @@ public class DimensionDataStorage {
@Nullable
private <T extends SavedData> T readSavedData(Function<CompoundTag, T> function, String id) {
try {
- File file = this.getDataFile(id);
- if (file.exists()) {
- CompoundTag compoundTag = this.readTagFromDisk(id, SharedConstants.getCurrentVersion().getWorldVersion());
- return function.apply(compoundTag.getCompound("data"));
+ CompoundTag compoundTag = this.readTagFromDisk(id, SharedConstants.getCurrentVersion().getWorldVersion());
+ if(compoundTag == null)
+ {
+ return null;
}
+ return function.apply(compoundTag.getCompound("data"));
} catch (Exception var5) {
LOGGER.error("Error loading saved data: {}", id, var5);
}
@@ -76,88 +76,19 @@ public class DimensionDataStorage {
this.cache.put(string, savedData);
}
- public CompoundTag readTagFromDisk(String id, int dataVersion) throws IOException {
+ public CompoundTag readTagFromDisk(String id, int dataVersion) throws IOException
+ {
File file = this.getDataFile(id);
- FileInputStream fileInputStream = new FileInputStream(file);
-
- CompoundTag var8;
- try {
- PushbackInputStream pushbackInputStream = new PushbackInputStream(fileInputStream, 2);
-
- try {
- CompoundTag compoundTag;
- if (this.isGzip(pushbackInputStream)) {
- compoundTag = NbtIo.readCompressed(pushbackInputStream);
- } else {
- DataInputStream dataInputStream = new DataInputStream(pushbackInputStream);
-
- try {
- compoundTag = NbtIo.read(dataInputStream);
- } catch (Throwable var13) {
- try {
- dataInputStream.close();
- } catch (Throwable var12) {
- var13.addSuppressed(var12);
- }
-
- throw var13;
- }
-
- dataInputStream.close();
- }
-
- int i = compoundTag.contains("DataVersion", 99) ? compoundTag.getInt("DataVersion") : 1343;
- var8 = NbtUtils.update(this.fixerUpper, DataFixTypes.SAVED_DATA, compoundTag, i, dataVersion);
- } catch (Throwable var14) {
- try {
- pushbackInputStream.close();
- } catch (Throwable var11) {
- var14.addSuppressed(var11);
- }
-
- throw var14;
- }
-
- pushbackInputStream.close();
- } catch (Throwable var15) {
- com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var15); // Paper
- try {
- fileInputStream.close();
- } catch (Throwable var10) {
- var15.addSuppressed(var10);
- }
-
- throw var15;
- }
-
- fileInputStream.close();
- return var8;
- }
-
- private boolean isGzip(PushbackInputStream pushbackInputStream) throws IOException {
- byte[] bs = new byte[2];
- boolean bl = false;
- int i = pushbackInputStream.read(bs, 0, 2);
- if (i == 2) {
- int j = (bs[1] & 255) << 8 | bs[0] & 255;
- if (j == 35615) {
- bl = true;
- }
- }
-
- if (i != 0) {
- pushbackInputStream.unread(bs, 0, i);
- }
-
- return bl;
+ return SkyPaper.INSTANCE.getWorldDataCacheDao().loadWorldDataCache(this.worldDatabaseId, file.getName());
}
- public void save() {
- this.cache.forEach((string, savedData) -> {
+ public void save()
+ {
+ this.cache.forEach((string, savedData) ->
+ {
if (savedData != null) {
- savedData.save(this.getDataFile(string));
+ savedData.save(this.worldDatabaseId, this.getDataFile(string));
}
-
});
}
}
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 b794c02ea36bdc901b1f6a160095abb3fcfe9b60..77d66864183b5d62446c0f00abac4d37a7dff9c9 100644
--- a/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java
+++ b/src/main/java/net/minecraft/world/level/storage/LevelStorageSource.java
@@ -33,6 +33,8 @@ import java.util.function.BiFunction;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.annotation.Nullable;
+
+import dev.cobblesword.skypaper.SkyPaper;
import net.minecraft.FileUtil;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
@@ -193,28 +195,24 @@ public class LevelStorageSource {
@Nullable
<T> T readLevelData(File file, BiFunction<File, DataFixer, T> levelDataParser) {
- if (!file.exists()) {
- return null;
- } else {
- File file1 = new File(file, "level.dat");
+ T t0 = levelDataParser.apply(file, this.fixerUpper);
- if (file1.exists()) {
- T t0 = levelDataParser.apply(file1, this.fixerUpper);
-
- if (t0 != null) {
- return t0;
- }
- }
-
- file1 = new File(file, "level.dat_old");
- return file1.exists() ? levelDataParser.apply(file1, this.fixerUpper) : null;
+ if (t0 != null) {
+ return t0;
}
+
+ return null;
}
@Nullable
private static DataPackConfig getDataPacks(File file, DataFixer datafixer) {
try {
- CompoundTag nbttagcompound = NbtIo.readCompressed(file);
+ CompoundTag nbttagcompound = SkyPaper.INSTANCE.getWorldDao().loadWorldData(file.getName());
+ if(nbttagcompound == null)
+ {
+ // don't generate world
+ return null;
+ }
CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
nbttagcompound1.remove("Player");
@@ -231,7 +229,13 @@ public class LevelStorageSource {
static BiFunction<File, DataFixer, PrimaryLevelData> getLevelData(DynamicOps<Tag> dynamicops, DataPackConfig datapackconfiguration) {
return (file, datafixer) -> {
try {
- CompoundTag nbttagcompound = NbtIo.readCompressed(file);
+ CompoundTag nbttagcompound = SkyPaper.INSTANCE.getWorldDao().loadWorldData(file.getName());
+ if(nbttagcompound == null)
+ {
+ // world not generated
+ return null;
+ }
+
CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
CompoundTag nbttagcompound2 = nbttagcompound1.contains("Player", 10) ? nbttagcompound1.getCompound("Player") : null;
@@ -253,7 +257,13 @@ public class LevelStorageSource {
BiFunction<File, DataFixer, LevelSummary> levelSummaryReader(File file, boolean locked) {
return (file1, datafixer) -> {
try {
- CompoundTag nbttagcompound = NbtIo.readCompressed(file1);
+ CompoundTag nbttagcompound = SkyPaper.INSTANCE.getWorldDao().loadWorldData(file.getName());
+ if(nbttagcompound == null)
+ {
+ // no world found
+ return null;
+ }
+
CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
nbttagcompound1.remove("Player");
@@ -322,8 +332,6 @@ public class LevelStorageSource {
// CraftBukkit end
public class LevelStorageAccess implements AutoCloseable {
-
- final DirectoryLock lock;
public final Path levelPath;
private final String levelId;
private final Map<LevelResource, Path> resources = Maps.newHashMap();
@@ -335,7 +343,6 @@ public class LevelStorageSource {
// CraftBukkit end
this.levelId = s;
this.levelPath = LevelStorageSource.this.baseDir.resolve(s);
- this.lock = DirectoryLock.create(this.levelPath);
}
public String getLevelId() {
@@ -358,14 +365,7 @@ public class LevelStorageSource {
return LevelStorageSource.getFolder(this.levelPath.toFile(), this.dimensionType); // CraftBukkit
}
- private void checkLock() {
- if (!this.lock.isValid()) {
- throw new IllegalStateException("Lock is no longer valid");
- }
- }
-
public PlayerDataStorage createPlayerStorage() {
- this.checkLock();
return new PlayerDataStorage(this, LevelStorageSource.this.fixerUpper);
}
@@ -376,25 +376,21 @@ public class LevelStorageSource {
}
public boolean convertLevel(ProgressListener progressListener) {
- this.checkLock();
return McRegionUpgrader.convertLevel(this, progressListener);
}
@Nullable
public LevelSummary getSummary() {
- this.checkLock();
return (LevelSummary) LevelStorageSource.this.readLevelData(this.levelPath.toFile(), LevelStorageSource.this.levelSummaryReader(this.levelPath.toFile(), false));
}
@Nullable
public WorldData getDataTag(DynamicOps<Tag> dynamicops, DataPackConfig datapackconfiguration) {
- this.checkLock();
return (WorldData) LevelStorageSource.this.readLevelData(this.levelPath.toFile(), LevelStorageSource.getLevelData(dynamicops, datapackconfiguration));
}
@Nullable
public DataPackConfig getDataPacks() {
- this.checkLock();
return (DataPackConfig) LevelStorageSource.this.readLevelData(this.levelPath.toFile(), LevelStorageSource::getDataPacks);
}
@@ -410,143 +406,15 @@ public class LevelStorageSource {
nbttagcompound2.put("Data", nbttagcompound1);
try {
- File file1 = File.createTempFile("level", ".dat", file);
-
- NbtIo.writeCompressed(nbttagcompound2, file1);
- File file2 = new File(file, "level.dat_old");
- File file3 = new File(file, "level.dat");
-
- Util.safeReplaceFile(file3, file1, file2);
+ SkyPaper.INSTANCE.getWorldDao().saveWorldData(file.getName(), nbttagcompound2);
} catch (Exception exception) {
LevelStorageSource.LOGGER.error("Failed to save level {}", file, exception);
}
-
- }
-
- public Optional<Path> getIconFile() {
- return !this.lock.isValid() ? Optional.empty() : Optional.of(this.levelPath.resolve("icon.png"));
- }
-
- public void deleteLevel() throws IOException {
- this.checkLock();
- final Path path = this.levelPath.resolve("session.lock");
- int i = 1;
-
- while (i <= 5) {
- LevelStorageSource.LOGGER.info("Attempt {}...", i);
-
- try {
- Files.walkFileTree(this.levelPath, new SimpleFileVisitor<Path>() {
- public FileVisitResult visitFile(Path path1, BasicFileAttributes basicfileattributes) throws IOException {
- if (!path1.equals(path)) {
- LevelStorageSource.LOGGER.debug("Deleting {}", path1);
- Files.delete(path1);
- }
-
- return FileVisitResult.CONTINUE;
- }
-
- public FileVisitResult postVisitDirectory(Path path1, IOException ioexception) throws IOException {
- if (ioexception != null) {
- throw ioexception;
- } else {
- if (path1.equals(LevelStorageAccess.this.levelPath)) {
- LevelStorageAccess.this.lock.close();
- Files.deleteIfExists(path);
- }
-
- Files.delete(path1);
- return FileVisitResult.CONTINUE;
- }
- }
- });
- break;
- } catch (IOException ioexception) {
- if (i >= 5) {
- throw ioexception;
- }
-
- LevelStorageSource.LOGGER.warn("Failed to delete {}", this.levelPath, ioexception);
-
- try {
- Thread.sleep(500L);
- } catch (InterruptedException interruptedexception) {
- ;
- }
-
- ++i;
- }
- }
-
- }
-
- public void renameLevel(String name) throws IOException {
- this.checkLock();
- File file = new File(LevelStorageSource.this.baseDir.toFile(), this.levelId);
-
- if (file.exists()) {
- File file1 = new File(file, "level.dat");
-
- if (file1.exists()) {
- CompoundTag nbttagcompound = NbtIo.readCompressed(file1);
- CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
-
- nbttagcompound1.putString("LevelName", name);
- NbtIo.writeCompressed(nbttagcompound, file1);
- }
-
- }
- }
-
- public long makeWorldBackup() throws IOException {
- this.checkLock();
- String s = LocalDateTime.now().format(LevelStorageSource.FORMATTER);
- String s1 = s + "_" + this.levelId;
- Path path = LevelStorageSource.this.getBackupPath();
-
- try {
- Files.createDirectories(Files.exists(path, new LinkOption[0]) ? path.toRealPath() : path);
- } catch (IOException ioexception) {
- throw new RuntimeException(ioexception);
- }
-
- Path path1 = path.resolve(FileUtil.findAvailableName(path, s1, ".zip"));
- final ZipOutputStream zipoutputstream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(path1)));
-
- try {
- final Path path2 = Paths.get(this.levelId);
-
- Files.walkFileTree(this.levelPath, new SimpleFileVisitor<Path>() {
- public FileVisitResult visitFile(Path path3, BasicFileAttributes basicfileattributes) throws IOException {
- if (path3.endsWith("session.lock")) {
- return FileVisitResult.CONTINUE;
- } else {
- String s2 = path2.resolve(LevelStorageAccess.this.levelPath.relativize(path3)).toString().replace('\\', '/');
- ZipEntry zipentry = new ZipEntry(s2);
-
- zipoutputstream.putNextEntry(zipentry);
- com.google.common.io.Files.asByteSource(path3.toFile()).copyTo(zipoutputstream);
- zipoutputstream.closeEntry();
- return FileVisitResult.CONTINUE;
- }
- }
- });
- } catch (Throwable throwable) {
- try {
- zipoutputstream.close();
- } catch (Throwable throwable1) {
- throwable.addSuppressed(throwable1);
- }
-
- throw throwable;
- }
-
- zipoutputstream.close();
- return Files.size(path1);
}
+ @Override
public void close() throws IOException {
- this.lock.close();
+
}
}
}
diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
index 35c39aed9583275ef25d32c783715798b52bdb63..5da951e1e9d2581b3cec28397f1286837fa9dee1 100644
--- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -22,13 +22,18 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
public class PlayerDataStorage {
private static final Logger LOGGER = LogManager.getLogger();
- private final File playerDir;
+ private File playerDir;
protected final DataFixer fixerUpper;
- public PlayerDataStorage(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer) {
+ public PlayerDataStorage(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer)
+ {
this.fixerUpper = dataFixer;
- this.playerDir = session.getLevelPath(LevelResource.PLAYER_DATA_DIR).toFile();
- this.playerDir.mkdirs();
+
+ if (!org.spigotmc.SpigotConfig.disablePlayerDataSaving)
+ {
+ this.playerDir = session.getLevelPath(LevelResource.PLAYER_DATA_DIR).toFile();
+ this.playerDir.mkdirs();
+ }
}
public void save(Player player) {
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
index aeffb30cd91d4b21850059d33070c537bd5cb25e..fe3d2ea6369f3661f4e79b50852498601fa18da2 100644
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
@@ -20,7 +20,7 @@ public class CraftWorldInfo implements WorldInfo {
public CraftWorldInfo(ServerLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager) {
this.name = worldDataServer.getLevelName();
- this.uuid = WorldUUID.getUUID(session.levelPath.toFile());
+ this.uuid = WorldUUID.getUUID(session.levelPath.toFile()).uuid;
this.environment = environment;
this.seed = ((PrimaryLevelData) worldDataServer).worldGenSettings().seed();
this.minHeight = dimensionManager.minY();
diff --git a/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java b/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java
index 738100ffa60647790f0921cf31d5bbc2714e27dd..59e90ba32fe318968b0a01706d22a496c34498e0 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java
@@ -7,6 +7,9 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;
+
+import dev.cobblesword.skypaper.SkyPaper;
+import dev.cobblesword.skypaper.database.dto.WorldDTO;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -17,42 +20,11 @@ public final class WorldUUID {
private WorldUUID() {
}
- public static UUID getUUID(File baseDir) {
- File file1 = new File(baseDir, "uid.dat");
- if (file1.exists()) {
- DataInputStream dis = null;
- try {
- dis = new DataInputStream(new FileInputStream(file1));
- return new UUID(dis.readLong(), dis.readLong());
- } catch (IOException ex) {
- WorldUUID.LOGGER.warn("Failed to read " + file1 + ", generating new random UUID", ex);
- } finally {
- if (dis != null) {
- try {
- dis.close();
- } catch (IOException ex) {
- // NOOP
- }
- }
- }
- }
- UUID uuid = UUID.randomUUID();
- DataOutputStream dos = null;
- try {
- dos = new DataOutputStream(new FileOutputStream(file1));
- dos.writeLong(uuid.getMostSignificantBits());
- dos.writeLong(uuid.getLeastSignificantBits());
- } catch (IOException ex) {
- WorldUUID.LOGGER.warn("Failed to write " + file1, ex);
- } finally {
- if (dos != null) {
- try {
- dos.close();
- } catch (IOException ex) {
- // NOOP
- }
- }
- }
- return uuid;
+ public static WorldDTO getUUID(File baseDir) {
+ return SkyPaper.INSTANCE.getWorldDao().loadOrCreateUID(baseDir.getName());
+ }
+
+ public static WorldDTO getDatabaseIdAndUID(File baseDir) {
+ return SkyPaper.INSTANCE.getWorldDao().loadOrCreateUID(baseDir.getName());
}
}
#Minecraft server properties
#Sun Oct 10 23:23:16 NZDT 2021
# My server.properties for empty worlds
enable-jmx-monitoring=false
rcon.port=25575
gamemode=survival
enable-command-block=false
enable-query=false
generator-settings={"lakes"\:false,"features"\:false,"layers"\:[{"block"\:"minecraft\:air","height"\:1}],"structures"\:{"structures"\:{}}}
level-name=world
motd=A Minecraft Server
query.port=25565
pvp=true
difficulty=easy
network-compression-threshold=256
require-resource-pack=false
max-tick-time=60000
use-native-transport=true
max-players=20
online-mode=true
enable-status=true
allow-flight=false
broadcast-rcon-to-ops=true
view-distance=10
server-ip=
resource-pack-prompt=
allow-nether=false
server-port=25565
enable-rcon=false
sync-chunk-writes=true
op-permission-level=4
prevent-proxy-connections=false
resource-pack=
entity-broadcast-range-percentage=100
rcon.password=
player-idle-timeout=0
debug=false
force-gamemode=false
rate-limit=0
hardcore=false
white-list=false
broadcast-console-to-ops=true
spawn-npcs=true
spawn-animals=true
snooper-enabled=true
function-permission-level=2
level-type=FLAT
text-filtering-config=
spawn-monsters=true
enforce-whitelist=false
resource-pack-sha1=
spawn-protection=0
max-world-size=29999984
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:39 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `world`
--
CREATE TABLE `world` (
`id` int(11) NOT NULL,
`level_id` varchar(255) NOT NULL,
`uid` varchar(36) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `world`
--
ALTER TABLE `world`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `world`
--
ALTER TABLE `world`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:40 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `worldchunk_entities`
--
CREATE TABLE `worldchunk_entities` (
`id` int(11) NOT NULL,
`world_id` int(11) NOT NULL,
`chunk_x` int(11) NOT NULL,
`chunk_z` int(11) NOT NULL,
`chunk_index` bigint(20) NOT NULL,
`nbt` mediumblob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `worldchunk_entities`
--
ALTER TABLE `worldchunk_entities`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `chunk_unique_per_world` (`world_id`,`chunk_index`),
ADD KEY `world_id_IX` (`world_id`) USING BTREE;
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `worldchunk_entities`
--
ALTER TABLE `worldchunk_entities`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:40 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `worldchunk_poi`
--
CREATE TABLE `worldchunk_poi` (
`id` int(11) NOT NULL,
`world_id` int(11) NOT NULL,
`chunk_x` int(11) NOT NULL,
`chunk_z` int(11) NOT NULL,
`chunk_index` bigint(20) NOT NULL,
`nbt` mediumblob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `worldchunk_poi`
--
ALTER TABLE `worldchunk_poi`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `chunk_unique_per_world` (`world_id`,`chunk_index`),
ADD KEY `world_id_IX` (`world_id`) USING BTREE;
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `worldchunk_poi`
--
ALTER TABLE `worldchunk_poi`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:41 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `worldchunk_region`
--
CREATE TABLE `worldchunk_region` (
`id` int(11) NOT NULL,
`world_id` int(11) NOT NULL,
`chunk_x` int(11) NOT NULL,
`chunk_z` int(11) NOT NULL,
`chunk_index` bigint(20) NOT NULL,
`nbt` mediumblob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `worldchunk_region`
--
ALTER TABLE `worldchunk_region`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `chunk_unique_per_world` (`world_id`,`chunk_index`),
ADD KEY `world_id_IX` (`world_id`) USING BTREE;
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `worldchunk_region`
--
ALTER TABLE `worldchunk_region`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:41 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `worlddatacache`
--
CREATE TABLE `worlddatacache` (
`id` int(11) NOT NULL,
`worlddata_id` int(11) NOT NULL,
`file_name` varchar(255) NOT NULL,
`nbt` blob NOT NULL,
`creation_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_timestamp` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `worlddatacache`
--
ALTER TABLE `worlddatacache`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `one_file_per_world` (`worlddata_id`,`file_name`),
ADD KEY `worlddata_id_IX` (`worlddata_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `worlddatacache`
--
ALTER TABLE `worlddatacache`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-- phpMyAdmin SQL Dump
-- version 4.9.5
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Oct 10, 2021 at 10:42 AM
-- Server version: 5.7.24
-- PHP Version: 7.4.1
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `skyblock`
--
-- --------------------------------------------------------
--
-- Table structure for table `worldproperties`
--
CREATE TABLE `worldproperties` (
`id` int(11) NOT NULL,
`level_id` varchar(255) NOT NULL,
`nbt` mediumblob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `worldproperties`
--
ALTER TABLE `worldproperties`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `level_id_IX` (`level_id`),
ADD UNIQUE KEY `level_id_is_unique` (`level_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `worldproperties`
--
ALTER TABLE `worldproperties`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment