diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperties.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperties.java index 92c4ac1c..98947880 100644 --- a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperties.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperties.java @@ -99,8 +99,11 @@ public class SlimeProperties { ); @ApiStatus.Experimental + @Deprecated(forRemoval = true) public static final SlimePropertyInt CHUNK_SECTION_MIN = SlimePropertyInt.create("chunkSectionMin", -4); + @ApiStatus.Experimental + @Deprecated(forRemoval = true) public static final SlimePropertyInt CHUNK_SECTION_MAX = SlimePropertyInt.create("chunkSectionMax", 19); /** diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java index 1c687ee3..4f14a723 100644 --- a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; /** * A Property Map object. @@ -35,6 +36,21 @@ public T getValue(final SlimeProperty property) { } } + /** + * Return the current value of the given property as an Optional + * Instead of returning the default value if the property is not set, it returns an empty Optional + * + * @param property The slime property + * @return An Optional containing the current value, or empty if not set + */ + public Optional getOptionalValue(final SlimeProperty property) { + if (this.properties.containsKey(property.getKey())) { + return Optional.of(property.readValue(property.cast(this.properties.get(property.getKey())))); + } else { + return Optional.empty(); + } + } + /** * Return the properties (CompoundMap) * diff --git a/aspaper-api/build.gradle.kts.patch b/aspaper-api/build.gradle.kts.patch index 3a701b38..1003af3b 100644 --- a/aspaper-api/build.gradle.kts.patch +++ b/aspaper-api/build.gradle.kts.patch @@ -1,6 +1,6 @@ --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts -@@ -40,6 +_,7 @@ +@@ -39,6 +_,7 @@ } dependencies { @@ -8,7 +8,7 @@ // api dependencies are listed transitively to API consumers api("com.google.guava:guava:33.3.1-jre") api("com.google.code.gson:gson:2.11.0") -@@ -90,7 +_,7 @@ +@@ -89,7 +_,7 @@ testRuntimeOnly("org.junit.platform:junit-platform-launcher") } @@ -17,7 +17,7 @@ idea { module { generatedSourceDirs.add(generatedDir.toFile()) -@@ -100,6 +_,18 @@ +@@ -99,6 +_,18 @@ main { java { srcDir(generatedDir) @@ -36,7 +36,7 @@ } } } -@@ -166,7 +_,7 @@ +@@ -165,7 +_,7 @@ tasks.withType().configureEach { val options = options as StandardJavadocDocletOptions @@ -45,7 +45,7 @@ options.use() options.isDocFilesSubDirs = true options.links( -@@ -199,11 +_,11 @@ +@@ -198,11 +_,11 @@ } // workaround for https://github.com/gradle/gradle/issues/4046 diff --git a/aspaper-server/build.gradle.kts.patch b/aspaper-server/build.gradle.kts.patch index 8e87751e..33bdedf7 100644 --- a/aspaper-server/build.gradle.kts.patch +++ b/aspaper-server/build.gradle.kts.patch @@ -1,6 +1,6 @@ --- a/paper-server/build.gradle.kts +++ b/paper-server/build.gradle.kts -@@ -26,6 +_,17 @@ +@@ -23,6 +_,17 @@ minecraftVersion = providers.gradleProperty("mcVersion") gitFilePatches = false @@ -17,8 +17,8 @@ + spigot { enabled = true - buildDataRef = "436eac9815c211be1a2a6ca0702615f995e81c44" -@@ -107,7 +_,19 @@ + buildDataRef = "42d18d4c4653ffc549778dbe223f6994a031d69e" +@@ -104,7 +_,19 @@ } } @@ -39,17 +39,17 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { extendsFrom(configurations.compileClasspath.get()) } -@@ -129,7 +_,8 @@ +@@ -127,7 +_,8 @@ } dependencies { - implementation(project(":paper-api")) + implementation(project(":aspaper-api")) //ASP + implementation(project(":core")) //ASP - implementation("ca.spottedleaf:concurrentutil:0.0.3") + implementation("ca.spottedleaf:concurrentutil:0.0.7") implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 -@@ -200,14 +_,14 @@ +@@ -197,14 +_,14 @@ val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", @@ -69,7 +69,7 @@ "Build-Number" to (build ?: ""), "Build-Time" to buildTime.toString(), "Git-Branch" to gitBranch, -@@ -266,7 +_,7 @@ +@@ -263,7 +_,7 @@ jvmArgumentProviders.add(provider) } diff --git a/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch b/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch index aa90d7f5..66372749 100644 --- a/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch +++ b/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Disable dragon battle diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 53ec0565a76663de74bb228c23c3d74a640a20f9..2c85ace929aeb9078fe868bcc56a2be08ac2f7ba 100644 +index efd599c516d89ebc5f040413b7c17c5bf0ad796c..fe30353d28cf7e35e7ba7267a8a630f2cffccd4a 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -684,7 +684,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -707,7 +707,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ); this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), this.structureCheck); // CraftBukkit if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END diff --git a/aspaper-server/minecraft-patches/features/0003-Avoid-IO-call-for-UUID.patch b/aspaper-server/minecraft-patches/features/0002-Avoid-IO-call-for-UUID.patch similarity index 79% rename from aspaper-server/minecraft-patches/features/0003-Avoid-IO-call-for-UUID.patch rename to aspaper-server/minecraft-patches/features/0002-Avoid-IO-call-for-UUID.patch index 419b68e9..fab9e859 100644 --- a/aspaper-server/minecraft-patches/features/0003-Avoid-IO-call-for-UUID.patch +++ b/aspaper-server/minecraft-patches/features/0002-Avoid-IO-call-for-UUID.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Avoid IO call for UUID diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 2c85ace929aeb9078fe868bcc56a2be08ac2f7ba..939605ce630ab66b7236962dec3ae61ed23dc222 100644 +index 2c8e5d8bebb53c70c3180e72af738e87ec0f36f6..c394731283ee3344a8e21d3b2d0f75470e4f6bfd 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -612,7 +612,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor - this.pvpMode = server.isPvpAllowed(); +@@ -636,7 +636,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // CraftBukkit start + super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs & Async-Anti-Xray: Pass executor this.levelStorageAccess = levelStorageAccess; - this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile()); + this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile()) : java.util.UUID.randomUUID(); //ASP - avoid IO calls + this.levelLoadListener = new net.minecraft.server.level.progress.LoggingLevelLoadListener(false, this); // CraftBukkit end this.tickTime = tickTime; - this.server = server; diff --git a/aspaper-server/minecraft-patches/features/0002-World-overrides.patch b/aspaper-server/minecraft-patches/features/0002-World-overrides.patch deleted file mode 100644 index e520cc59..00000000 --- a/aspaper-server/minecraft-patches/features/0002-World-overrides.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Mon, 26 Dec 2022 11:25:35 -0500 -Subject: [PATCH] World overrides - - -diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 3349baa2ab971db435b88083688bd0f99474607e..610e58bede7e1482fc861ab6559f4f3a6e7e3633 100644 ---- a/net/minecraft/server/MinecraftServer.java -+++ b/net/minecraft/server/MinecraftServer.java -@@ -516,18 +516,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor -+ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> bootstrap != null ? com.infernalsuite.asp.config.SlimePaperWorldConfig.initializeOrGet() : server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor //ASP - Optimize world config - this.pvpMode = server.isPvpAllowed(); +- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs & Async-Anti-Xray: Pass executor ++ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> bootstrap != null ? com.infernalsuite.asp.config.SlimePaperWorldConfig.initializeOrGet() : server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs & Async-Anti-Xray: Pass executor //ASP - Optimize world config this.levelStorageAccess = levelStorageAccess; this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile()) : java.util.UUID.randomUUID(); //ASP - avoid IO calls + this.levelLoadListener = new net.minecraft.server.level.progress.LoggingLevelLoadListener(false, this); diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index e4b9a564aad3d9b673808caa18265b06592ceab8..8bb30eba86b74f155ef9580dc6c01d761e23dd5a 100644 +index d225267b1b07854b76c88ce461d7817a86d023a0..de152aca205954d33cf2997043cb8c91b9587cdf 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -838,7 +838,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl +@@ -844,7 +844,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl this.maxSectionY = this.maxY >> 4; this.sectionsCount = this.maxSectionY - this.minSectionY + 1; // Paper end - getblock optimisations - cache world height/sections diff --git a/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch b/aspaper-server/minecraft-patches/features/0004-Read-only-dimension-data-store.patch similarity index 91% rename from aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch rename to aspaper-server/minecraft-patches/features/0004-Read-only-dimension-data-store.patch index f1279fa1..432286d3 100644 --- a/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch +++ b/aspaper-server/minecraft-patches/features/0004-Read-only-dimension-data-store.patch @@ -5,11 +5,11 @@ Subject: [PATCH] Read only dimension data store diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 5d63bf024cbcbd2f627c64fee77553c9a512bd15..da169d319e9c09de2d5a34784af4e4c133b10f46 100644 +index 413f0673557b9e3f9177d15e9bef61bded209e34..ebc9373bb3a48ef41d54d445c1aa02fe06d83cff 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -210,7 +210,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - LOGGER.error("Failed to create dimension data storage directory", (Throwable)var15); + LOGGER.error("Failed to create dimension data storage directory", (Throwable)var14); } - this.dataStorage = new DimensionDataStorage(new SavedData.Context(level), path, fixerUpper, level.registryAccess()); diff --git a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch index 68303fee..120dff2f 100644 --- a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch @@ -1,6 +1,6 @@ --- a/ca/spottedleaf/moonrise/paper/PaperHooks.java +++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java -@@ -206,6 +_,7 @@ +@@ -214,6 +_,7 @@ @Override public boolean forceNoSave(final ChunkAccess chunk) { diff --git a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch index 8579614f..a31726a5 100644 --- a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch @@ -1,6 +1,6 @@ --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -@@ -188,7 +_,9 @@ +@@ -191,7 +_,9 @@ }; } diff --git a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch index d5af1d38..ca2371ce 100644 --- a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch @@ -1,6 +1,6 @@ --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -@@ -912,13 +_,24 @@ +@@ -887,6 +_,12 @@ } if (chunk instanceof LevelChunk levelChunk) { @@ -12,21 +12,22 @@ + //ASP end this.world.unload(levelChunk); } -+ - } +@@ -896,8 +_,11 @@ // unload entity data if (entityChunk != null) { -- this.saveEntities(entityChunk, true); + final Completable[] entityWrite = new Completable[1]; +- this.saveEntities(entityChunk, true, entityWrite); +- + //ASP start - prevent saving + if(!(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance)) { -+ this.saveEntities(entityChunk, true); ++ this.saveEntities(entityChunk, true, entityWrite); + } + //ASP end - prevent saving - // yes this is a hack to pass the compound tag through... - final CompoundTag lastEntityUnload = this.lastEntityUnload; - this.lastEntityUnload = null; -@@ -1717,6 +_,12 @@ + if (entityChunk.unload()) { + final ReentrantAreaLock.Node schedulingLock = this.scheduler.schedulingLockArea.lock(this.chunkX, this.chunkZ); + try { +@@ -1682,6 +_,12 @@ boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved())); boolean canSavePOI = !forceNoSaveChunk && (poi != null && poi.isDirty()); boolean canSaveEntities = entities != null; @@ -38,4 +39,4 @@ + //ASP end - prevent saving if (canSaveChunk) { - canSaveChunk = this.saveChunk(chunk, false); + canSaveChunk = this.saveChunk(chunk, false, null); diff --git a/aspaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/aspaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch index ed0f1226..ec55b661 100644 --- a/aspaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch +++ b/aspaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -565,7 +_,32 @@ +@@ -592,7 +_,31 @@ } // Paper end - chunk tick iteration @@ -14,7 +14,6 @@ + net.minecraft.world.level.storage.PrimaryLevelData serverLevelData, // CraftBukkit + ResourceKey dimension, + LevelStem levelStem, -+ ChunkProgressListener progressListener, + boolean isDebug, + long biomeZoomSeed, + List customSpawners, @@ -25,7 +24,7 @@ + org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit + ) { + //ASP start -+ this(null, server, dispatcher, levelStorageAccess, serverLevelData, dimension, levelStem, progressListener, ++ this(null, server, dispatcher, levelStorageAccess, serverLevelData, dimension, levelStem, + isDebug, biomeZoomSeed, customSpawners, tickTime, randomSequences, env, gen, biomeProvider); + } + @@ -34,15 +33,15 @@ MinecraftServer server, Executor dispatcher, LevelStorageSource.LevelStorageAccess levelStorageAccess, -@@ -582,6 +_,7 @@ +@@ -608,6 +_,7 @@ org.bukkit.generator.ChunkGenerator gen, // CraftBukkit org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit ) { + //ASP end // CraftBukkit start - super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor - this.pvpMode = server.isPvpAllowed(); -@@ -609,6 +_,13 @@ + super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs & Async-Anti-Xray: Pass executor + this.levelStorageAccess = levelStorageAccess; +@@ -635,6 +_,13 @@ chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); } // CraftBukkit end @@ -56,7 +55,7 @@ boolean flag = server.forceSynchronousWrites(); DataFixer fixerUpper = server.getFixerUpper(); // Paper - rewrite chunk system -@@ -689,6 +_,12 @@ +@@ -713,6 +_,12 @@ public void setDragonFight(@Nullable EndDragonFight dragonFight) { this.dragonFight = dragonFight; } diff --git a/aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch b/aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch index e945f425..3f53a519 100644 --- a/aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch +++ b/aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Delete temp folder after world is unloaded diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 185bfb619767a2ce8b831b083d946e9f8e7bfb0c..a920263b6eef436abe113d1264a782e458748728 100644 +index 7a8399c5f5ea404e31b2a907c7324cab5f9b4699..c56d700bd879bcaf785cacad07c7562f391f9e18 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1460,6 +1460,12 @@ public final class CraftServer implements Server { +@@ -1357,6 +1357,12 @@ public final class CraftServer implements Server { handle.getChunkSource().close(save); io.papermc.paper.FeatureHooks.closeEntityManager(handle, save); // SPIGOT-6722: close entityManager // Paper - chunk system handle.levelStorageAccess.close(); diff --git a/aspaper-server/paper-patches/features/0006-World-overrides.patch b/aspaper-server/paper-patches/features/0006-World-overrides.patch new file mode 100644 index 00000000..8e3cb747 --- /dev/null +++ b/aspaper-server/paper-patches/features/0006-World-overrides.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David +Date: Thu, 18 Sep 2025 20:28:39 +0200 +Subject: [PATCH] World overrides + + +diff --git a/src/main/java/io/papermc/paper/world/PaperWorldLoader.java b/src/main/java/io/papermc/paper/world/PaperWorldLoader.java +index f5e9dd5880681cf4c9b442fbd2e0e9aad1cfb804..6de0f58a8c1a08393276ecaba5b8e7e9640bdbf6 100644 +--- a/src/main/java/io/papermc/paper/world/PaperWorldLoader.java ++++ b/src/main/java/io/papermc/paper/world/PaperWorldLoader.java +@@ -3,6 +3,7 @@ package io.papermc.paper.world; + import com.google.common.io.Files; + import com.mojang.logging.LogUtils; + import com.mojang.serialization.Dynamic; ++import net.minecraft.core.Registry; + import net.minecraft.core.registries.Registries; + import net.minecraft.nbt.NbtException; + import net.minecraft.nbt.ReportedNbtException; +@@ -108,7 +109,21 @@ public record PaperWorldLoader(MinecraftServer server, String levelId) { + + // Loosely modeled on code in net.minecraft.server.Main + public void loadInitialWorlds() { +- for (final LevelStem stem : this.server.registryAccess().lookupOrThrow(Registries.LEVEL_STEM)) { ++ //ASP start - overwrite vanilla worlds if needed ++ Registry registry = this.server.registryAccess().lookupOrThrow(Registries.LEVEL_STEM); ++ for (final LevelStem stem : registry) { ++ ResourceKey levelStemKey = registry.getResourceKey(stem).get(); ++ if(levelStemKey == LevelStem.NETHER && com.infernalsuite.asp.SlimeNMSBridgeImpl.instance().loadNetherOverride()) { ++ continue; ++ } ++ if(levelStemKey == LevelStem.END && com.infernalsuite.asp.SlimeNMSBridgeImpl.instance().loadEndOverride()) { ++ continue; ++ } ++ if(levelStemKey == LevelStem.OVERWORLD && com.infernalsuite.asp.SlimeNMSBridgeImpl.instance().loadOverworldOverride()) { ++ continue; ++ } ++ //ASP end - overwrite vanilla worlds if needed ++ + final WorldLoadingInfo info = this.getWorldInfo(this.levelId, stem); + this.migrateWorldFolder(info); + if (!info.enabled()) { diff --git a/aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch b/aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch index 3046e74a..7e591c25 100644 --- a/aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch +++ b/aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -1,6 +1,6 @@ --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1450,6 +_,8 @@ +@@ -1347,6 +_,8 @@ return false; } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java b/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java index 3fb8c8ca..188e13b7 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java @@ -61,11 +61,11 @@ public SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOn long start = System.currentTimeMillis(); - LOGGER.info("Reading world {}.", worldName); + LOGGER.debug("Reading world {}.", worldName); byte[] serializedWorld = loader.readWorld(worldName); SlimeWorld slimeWorld = SlimeWorldReaderRegistry.readWorld(loader, worldName, serializedWorld, propertyMap, readOnly); - LOGGER.info("Applying datafixers for {}.", worldName); + LOGGER.debug("Applying datafixers for {}.", worldName); SlimeWorld dataFixed = SlimeNMSBridge.instance().getSlimeDataConverter().applyDataFixers(slimeWorld); // If the dataFixed and slimeWorld are same, then no datafixers were applied @@ -86,7 +86,7 @@ public SlimeWorldInstance loadWorld(SlimeWorld world, boolean callWorldLoadEvent throw new IllegalArgumentException("World " + world.getName() + " is already loaded"); } - LOGGER.info("Loading world {}...", world.getName()); + LOGGER.debug("Loading world {}...", world.getName()); long start = System.currentTimeMillis(); SlimeWorldInstance instance = BRIDGE_INSTANCE.loadInstance(world); diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java b/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java index 5704e8ba..71a452cf 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java @@ -169,7 +169,7 @@ public int getCurrentVersion() { public void registerWorld(SlimeLevelInstance server) { MinecraftServer mcServer = MinecraftServer.getServer(); - mcServer.initWorld(server, server.serverLevelData, mcServer.getWorldData(), server.serverLevelData.worldGenOptions()); + mcServer.initWorld(server, server.serverLevelData, server.serverLevelData.worldGenOptions()); mcServer.addLevel(server); } @@ -209,10 +209,10 @@ private PrimaryLevelData createWorldData(SlimeWorld world) { MinecraftServer mcServer = MinecraftServer.getServer(); DedicatedServerProperties serverProps = ((DedicatedServer) mcServer).getProperties(); String worldName = world.getName(); - WorldLoader.DataLoadContext context = mcServer.worldLoader; + WorldLoader.DataLoadContext context = mcServer.worldLoaderContext; - LevelSettings worldsettings = new LevelSettings(worldName, serverProps.gamemode, false, serverProps.difficulty, - true, new GameRules(context.dataConfiguration().enabledFeatures()), mcServer.worldLoader.dataConfiguration()); + LevelSettings worldsettings = new LevelSettings(worldName, serverProps.gameMode.get(), false, serverProps.difficulty.get(), + true, new GameRules(context.dataConfiguration().enabledFeatures()), mcServer.worldLoaderContext.dataConfiguration()); WorldOptions worldoptions = new WorldOptions(0, false, false); diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java b/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java index 9087e71d..3b246b08 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java @@ -32,7 +32,7 @@ private static void initialize( ) { SpigotWorldConfig spigotWorldConfig = new SpigotWorldConfig("asp-slimeworld"); - GameRules gameRules = new GameRules(server.worldLoader.dataConfiguration().enabledFeatures()); + GameRules gameRules = new GameRules(server.worldLoaderContext.dataConfiguration().enabledFeatures()); Configurations.ContextMap contextMap = PaperConfigurations.createWorldContextMap( /* diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelInstance.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelInstance.java index dc697381..4f52abbe 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelInstance.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelInstance.java @@ -16,9 +16,12 @@ import com.infernalsuite.asp.api.world.SlimeWorldInstance; import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.mojang.logging.LogUtils; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.util.TriState; import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -26,6 +29,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; import net.minecraft.util.ProgressListener; import net.minecraft.util.datafix.DataFixers; import net.minecraft.world.Difficulty; @@ -35,6 +39,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.storage.RegionStorageInfo; import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.validation.DirectoryValidator; @@ -43,6 +48,7 @@ import org.bukkit.event.world.WorldSaveEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; import org.spigotmc.AsyncCatcher; import java.io.IOException; @@ -55,12 +61,14 @@ import java.util.UUID; import java.util.concurrent.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; public class SlimeLevelInstance extends ServerLevel { public static LevelStorageSource CUSTOM_LEVEL_STORAGE; + private static final Logger LOGGER = LogUtils.getClassLogger(); static { try { @@ -87,8 +95,7 @@ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primar super(slimeBootstrap, MinecraftServer.getServer(), MinecraftServer.getServer().executor, CUSTOM_LEVEL_STORAGE.createAccess(slimeBootstrap.initial().getName() + UUID.randomUUID(), dimensionKey), - primaryLevelData, worldKey, worldDimension, - MinecraftServer.getServer().progressListenerFactory.create(11), false, 0, + primaryLevelData, worldKey, worldDimension, false, 0, Collections.emptyList(), true, null, environment, null, null); this.slimeInstance = new SlimeInMemoryWorld(slimeBootstrap, this); @@ -96,11 +103,21 @@ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primar SlimePropertyMap propertyMap = slimeBootstrap.initial().getPropertyMap(); this.serverLevelData.setDifficulty(Difficulty.valueOf(propertyMap.getValue(SlimeProperties.DIFFICULTY).toUpperCase())); - this.serverLevelData.setSpawn(new BlockPos( - propertyMap.getValue(SlimeProperties.SPAWN_X), - propertyMap.getValue(SlimeProperties.SPAWN_Y), - propertyMap.getValue(SlimeProperties.SPAWN_Z)), - propertyMap.getValue(SlimeProperties.SPAWN_YAW)); + serverLevelData.setSpawn( + new LevelData.RespawnData( + GlobalPos.of( + ResourceKey.create(Registries.DIMENSION, this.dimension().location()), + new BlockPos( + propertyMap.getValue(SlimeProperties.SPAWN_X), + propertyMap.getValue(SlimeProperties.SPAWN_Y), + propertyMap.getValue(SlimeProperties.SPAWN_Z) + ) + ), + Mth.wrapDegrees(propertyMap.getValue(SlimeProperties.SPAWN_YAW)), + Mth.wrapDegrees(0F) + ) + ); + super.chunkSource.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); ConcurrentMap extraData = this.slimeInstance.getExtraData(); @@ -109,7 +126,7 @@ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primar getWorld().readBukkitValues(Converter.convertTag(extraData.get("BukkitValues"))); } - this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); + this.pvpMode = propertyMap.getOptionalValue(SlimeProperties.PVP).map(TriState::byBoolean).orElse(TriState.NOT_SET); this.entityDataController = new SlimeEntityDataLoader( new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController.EntityRegionFileStorage( @@ -155,7 +172,6 @@ public Future save() { Bukkit.getPluginManager().callEvent(new WorldSaveEvent(getWorld())); //this.getChunkSource().save(forceSave); - this.serverLevelData.setWorldBorder(this.getWorldBorder().createSettings()); this.serverLevelData.setCustomBossEvents(MinecraftServer.getServer().getCustomBossEvents().save(MinecraftServer.getServer().registryAccess())); if (MinecraftServer.getServer().isStopped()) { // Make sure the world gets saved before stopping the server by running it from the main thread @@ -165,7 +181,7 @@ public Future save() { } } } catch (Throwable e) { - Bukkit.getLogger().log(Level.SEVERE, "There was a problem saving the SlimeLevelInstance " + serverLevelData.getLevelName(), e); + LOGGER.error("There was a problem saving the SlimeLevelInstance {}", serverLevelData.getLevelName(), e); return CompletableFuture.failedFuture(e); } return CompletableFuture.completedFuture(null); @@ -174,7 +190,7 @@ public Future save() { private Future saveInternal() { synchronized (saveLock) { // Don't want to save the SlimeWorld from multiple threads simultaneously SlimeWorldInstance slimeWorld = this.slimeInstance; - Bukkit.getLogger().log(Level.INFO, "Saving world " + this.slimeInstance.getName() + "..."); + LOGGER.debug("Saving world {}...", this.slimeInstance.getName()); long start = System.currentTimeMillis(); SlimeWorld world = this.slimeInstance.getSerializableCopy(); @@ -183,9 +199,9 @@ private Future saveInternal() { byte[] serializedWorld = SlimeSerializer.serialize(world); long saveStart = System.currentTimeMillis(); slimeWorld.getLoader().saveWorld(slimeWorld.getName(), serializedWorld); - Bukkit.getLogger().log(Level.INFO, "World " + slimeWorld.getName() + " serialized in " + (saveStart - start) + "ms and saved in " + (System.currentTimeMillis() - saveStart) + "ms."); + LOGGER.debug("World {} serialized in {}ms and saved in {}ms.", slimeWorld.getName(), saveStart - start, System.currentTimeMillis() - saveStart); } catch (Exception ex) { - Bukkit.getLogger().log(Level.SEVERE, "There was an issue saving world " + slimeWorld.getName() + " asynchronously.", ex); + LOGGER.error("There was an issue saving world {} asynchronously.", slimeWorld.getName(), ex); } }); @@ -200,17 +216,6 @@ public ChunkDataLoadTask getLoadTask(ChunkLoadTask task, ChunkTaskScheduler sche return new ChunkDataLoadTask(task, scheduler, world, chunkX, chunkZ, priority, onRun); } - @Override - public void setDefaultSpawnPos(BlockPos pos, float angle) { - super.setDefaultSpawnPos(pos, angle); - - SlimePropertyMap propertyMap = this.slimeInstance.getPropertyMap(); - propertyMap.setValue(SlimeProperties.SPAWN_X, pos.getX()); - propertyMap.setValue(SlimeProperties.SPAWN_Y, pos.getY()); - propertyMap.setValue(SlimeProperties.SPAWN_Z, pos.getZ()); - propertyMap.setValue(SlimeProperties.SPAWN_YAW, angle); - } - public void deleteTempFiles() { WORLD_SAVER_SERVICE.execute(() -> { Path path = this.levelStorageAccess.levelDirectory.path(); @@ -242,7 +247,7 @@ public void deleteTempFiles() { } }); } catch (IOException e) { - Bukkit.getLogger().log(Level.WARNING, "Unable to delete temp level directory" , e); + LOGGER.warn("Unable to delete temp level directory" , e); } }); } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/NMSSlimeChunk.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/NMSSlimeChunk.java index 39e80bfa..8165f80a 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/NMSSlimeChunk.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/NMSSlimeChunk.java @@ -16,6 +16,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; @@ -64,10 +65,7 @@ public SlimeChunkSection[] getSections() { SlimeChunkSection[] sections = new SlimeChunkSection[this.chunk.getSectionsCount()]; LevelLightEngine lightEngine = chunk.getLevel().getChunkSource().getLightEngine(); - Registry biomeRegistry = chunk.biomeRegistry; - - Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), - PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow()); + Registry biomeRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.BIOME); for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { LevelChunkSection section = chunk.getSections()[sectionId]; @@ -79,7 +77,8 @@ public SlimeChunkSection[] getSections() { // Sky light Nibble Array NibbleArray skyLightArray = Converter.convertArray(lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunk.getPos(), sectionId))); - sections[sectionId] = SlimeChunkConverter.convertChunkSection(codec, section, blockLightArray, skyLightArray); + sections[sectionId] = SlimeChunkConverter.convertChunkSection(chunk.level.palettedContainerFactory().biomeContainerCodec(), + chunk.level.palettedContainerFactory().blockStatesContainerCodec(), section, blockLightArray, skyLightArray); } return sections; diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/PartiallySerializedSlimeChunk.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/PartiallySerializedSlimeChunk.java index df7aecb0..89b3b480 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/PartiallySerializedSlimeChunk.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/PartiallySerializedSlimeChunk.java @@ -11,6 +11,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; @@ -30,7 +31,7 @@ * to be serialized async. This has a performance advantage over serializing to nbt and then storing it async later. */ public record PartiallySerializedSlimeChunk( - Registry biomeRegistry, + PalettedContainerFactory containerFactory, int x, int z, PartiallySerializedSlimeChunkSection[] sections, @@ -51,7 +52,6 @@ public record PartiallySerializedSlimeChunk( public static PartiallySerializedSlimeChunk of(NMSSlimeChunk slimeChunk, boolean saveBlockTicks, boolean saveFluidTicks, boolean savePoi) { LevelChunk chunk = slimeChunk.getChunk(); - Registry biomes = chunk.biomeRegistry; PartiallySerializedSlimeChunkSection[] sections = new PartiallySerializedSlimeChunkSection[chunk.getSectionsCount()]; LevelLightEngine lightEngine = chunk.getLevel().getChunkSource().getLightEngine(); @@ -90,7 +90,7 @@ public static PartiallySerializedSlimeChunk of(NMSSlimeChunk slimeChunk, boolean extra.put("ChunkBukkitValues", adventureTag); return new PartiallySerializedSlimeChunk( - biomes, + chunk.level.palettedContainerFactory(), chunk.locX, chunk.locZ, sections, @@ -117,15 +117,12 @@ public int getZ() { @Override public SlimeChunkSection[] getSections() { - Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), - PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow()); - SlimeChunkSection[] chunkSections = new SlimeChunkSection[this.sections.length]; for (int i = 0; i < this.sections.length; i++) { PartiallySerializedSlimeChunkSection partial = this.sections[i]; - chunkSections[i] = SlimeChunkConverter.convertChunkSection(codec, partial.section, partial.blockLight, partial.skyLight); + chunkSections[i] = SlimeChunkConverter.convertChunkSection(containerFactory.biomeContainerCodec(), containerFactory.blockStatesContainerCodec(), partial.section, partial.blockLight, partial.skyLight); } return chunkSections; diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/SlimeChunkConverter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/SlimeChunkConverter.java index 837e8fb8..277219ba 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/SlimeChunkConverter.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/chunk/SlimeChunkConverter.java @@ -31,6 +31,7 @@ import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.feature.foliageplacers.PineFoliagePlacer; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.SavedTick; @@ -47,16 +48,17 @@ public class SlimeChunkConverter { // Optimized empty section serialization static { + PalettedContainerFactory factory = PalettedContainerFactory.create(net.minecraft.server.MinecraftServer.getServer().registryAccess()); { - PalettedContainer empty = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); - Tag tag = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); + PalettedContainer empty = new PalettedContainer<>(Blocks.AIR.defaultBlockState(),factory.blockStatesStrategy(), null); + Tag tag = factory.blockStatesContainerCodec().encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); EMPTY_BLOCK_STATE_PALETTE = Converter.convertTag(tag); } { Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME); - PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); - Tag tag = SerializableChunkData.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); + PalettedContainer> empty = new PalettedContainer<>(biomes.get(Biomes.PLAINS).orElseThrow(), factory.biomeStrategy(), null); + Tag tag = factory.biomeContainerRWCodec().encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); EMPTY_BIOME_PALETTE = Converter.convertTag(tag); } @@ -80,8 +82,7 @@ public static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, Registry biomeRegistry = instance.registryAccess().lookupOrThrow(Registries.BIOME); - Codec>> codec = PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), - biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow(), null); + Codec>> codec = instance.palettedContainerFactory().biomeContainerRWCodec(); for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { SlimeChunkSection slimeSection = chunk.getSections()[sectionId]; @@ -99,12 +100,12 @@ public static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, PalettedContainer blockPalette; if (slimeSection.getBlockStatesTag() != null) { - DataResult> dataresult = SerializableChunkData.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { + DataResult> dataresult = instance.palettedContainerFactory().blockStatesContainerCodec().parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging }); blockPalette = dataresult.getOrThrow(); // todo proper logging } else { - blockPalette = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); + blockPalette = new PalettedContainer<>(Blocks.AIR.defaultBlockState(), instance.palettedContainerFactory().blockStatesStrategy(), null); } PalettedContainer> biomePalette; @@ -115,7 +116,7 @@ public static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, }); biomePalette = dataresult.getOrThrow(); // todo proper logging } else { - biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); + biomePalette = new PalettedContainer<>(biomeRegistry.get(Biomes.PLAINS).orElseThrow(), instance.palettedContainerFactory().biomeStrategy(), null); } if (sectionId < sections.length) { @@ -196,13 +197,13 @@ public static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, return nmsChunk; } - public static SlimeChunkSection convertChunkSection(Codec>> codec, LevelChunkSection section, NibbleArray blockLightArray, NibbleArray skyLightArray) { + public static SlimeChunkSection convertChunkSection(Codec>> biomeCodec, Codec> blockCodec, LevelChunkSection section, NibbleArray blockLightArray, NibbleArray skyLightArray) { // Block Data CompoundBinaryTag blockStateTag; if (section.hasOnlyAir()) { blockStateTag = EMPTY_BLOCK_STATE_PALETTE; } else { - Tag data = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling + Tag data = blockCodec.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling blockStateTag = Converter.convertTag(data); } @@ -212,7 +213,7 @@ public static SlimeChunkSection convertChunkSection(Codec h.is(Biomes.PLAINS))) { biomeTag = EMPTY_BIOME_PALETTE; } else { - Tag biomeData = codec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(); // todo error handling + Tag biomeData = biomeCodec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(); // todo error handling biomeTag = Converter.convertTag(biomeData); } diff --git a/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java b/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java index 07ec8295..f3bf31d7 100644 --- a/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java +++ b/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java @@ -75,6 +75,11 @@ public void set(@NotNull NamespacedKey key, @NotNull PersistentDataType

boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { Preconditions.checkNotNull(key, "The key cannot be null"); diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilWorldReader.java b/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilWorldReader.java index 65aec0d2..dfd6c072 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilWorldReader.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilWorldReader.java @@ -71,7 +71,7 @@ public SlimeWorld readFromData(AnvilImportData importData) { propertyMap.setValue(SlimeProperties.ENVIRONMENT, "normal"); if (!doesWorldContainRegion(worldDir)) { environmentDir = worldDir.resolve("DIM-1"); - propertyMap. setValue (SlimeProperties. ENVIRONMENT, "nether"); + propertyMap.setValue(SlimeProperties.ENVIRONMENT, "nether"); if (!doesWorldContainRegion(environmentDir)) { environmentDir = worldDir.resolve("DIM1"); @@ -91,7 +91,7 @@ public SlimeWorld readFromData(AnvilImportData importData) { try (var stream = Files.newDirectoryStream(regionDir, path -> path.toString().endsWith(".mca"))) { for (final Path path : stream) { LOGGER.info("Loading region file {}...", path.getFileName()); - chunks.putAll(loadChunks(path, worldVersion).stream() + chunks.putAll(loadChunks(path, worldVersion, propertyMap).stream() .collect(Collectors.toMap(chunk -> Util.chunkPosition(chunk.getX(), chunk.getZ()), Function.identity()))); } } @@ -187,7 +187,7 @@ private static void loadEntities(Path path, int version, Long2ObjectMap loadChunks(Path path, int worldVersion) throws IOException { + private static List loadChunks(Path path, int worldVersion, SlimePropertyMap propertyMap) throws IOException { byte[] regionByteArray = Files.readAllBytes(path); DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(regionByteArray)); @@ -204,6 +204,23 @@ private static List loadChunks(Path path, int worldVersion) throws I } } + int worldHeight; + int minY; + switch (propertyMap.getValue(SlimeProperties.ENVIRONMENT)) { + case "normal" -> { + worldHeight = 384; + minY = -64; + } + case "nether", "the_end" -> { + worldHeight = 256; + minY = 0; + } + case null, default -> throw new IllegalStateException("Unsupported environment, cant obtain world height data"); + } + + int minSectionY = minY >> 4; + int maxSectionY = (minY + worldHeight - 1) >> 4; + return chunks.stream().map((entry) -> { try { @@ -215,7 +232,7 @@ private static List loadChunks(Path path, int worldVersion) throws I DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset() + 5, chunkSize)); InputStream decompressorStream = compressionScheme == 1 ? new GZIPInputStream(chunkStream) : new InflaterInputStream(chunkStream); CompoundBinaryTag tag = BinaryTagIO.unlimitedReader().read(decompressorStream); - return readChunk(tag, worldVersion); + return readChunk(tag, worldVersion, minSectionY, maxSectionY); } catch (IOException ex) { throw new RuntimeException(ex); } @@ -260,7 +277,7 @@ private static void readEntityChunk(CompoundBinaryTag compound, int worldVersion } } - private static SlimeChunk readChunk(CompoundBinaryTag compound, int worldVersion) { + private static SlimeChunk readChunk(CompoundBinaryTag compound, int worldVersion, int minSectionY, int maxSectionY) { int chunkX = compound.getInt("xPos"); int chunkZ = compound.getInt("zPos"); @@ -285,39 +302,36 @@ private static SlimeChunk readChunk(CompoundBinaryTag compound, int worldVersion List entities = compound.getList("entities", BinaryTagTypes.COMPOUND).stream().map(t -> (CompoundBinaryTag) t).toList(); ListBinaryTag sectionsTag = compound.getList("sections", BinaryTagTypes.COMPOUND); - int minSectionY = compound.getInt("yPos"); - // TODO - look into this +1 below - int maxSectionY = sectionsTag.stream().map(tag -> ((CompoundBinaryTag) tag).getByte("Y")).max(Byte::compareTo).orElse((byte) 0) + 1; // Add 1 to the section, as we serialize it with the 1 added. - - SlimeChunkSection[] sectionArray = new SlimeChunkSection[maxSectionY - minSectionY]; + SlimeChunkSection[] sectionArray = new SlimeChunkSection[maxSectionY - minSectionY + 1 /* See LevelHeightAccessor getSectionsCount */]; + boolean hasSection = false; for (final BinaryTag rawRag : sectionsTag) { final CompoundBinaryTag sectionTag = (CompoundBinaryTag) rawRag; int index = sectionTag.getByte("Y"); + if(index < minSectionY || index > maxSectionY) continue; CompoundBinaryTag blockStatesTag = sectionTag.getCompound("block_states"); CompoundBinaryTag biomesTag = sectionTag.getCompound("biomes"); // TODO - actually, the section is empty if the block_states palette only contains air so uh... yeah xD fix this :P // NB - maybe consider an import flag to respect the original biome even if its an empty section, or just strip and replace with the world default - if (blockStatesTag.size() == 0 && biomesTag.size() == 0) continue; // Empty section + if (blockStatesTag.isEmpty() && biomesTag.isEmpty()) continue; // Empty section or light only section NibbleArray blockLightArray = applyByteArrayOrNull(sectionTag, "BlockLight", NibbleArray::new); NibbleArray skyLightArray = applyByteArrayOrNull(sectionTag, "SkyLight", NibbleArray::new); sectionArray[index - minSectionY] = new SlimeChunkSectionSkeleton(blockStatesTag, biomesTag, blockLightArray, skyLightArray); + hasSection = true; } Map extraTag = new HashMap<>(); CompoundBinaryTag chunkBukkitValues = compound.getCompound("ChunkBukkitValues"); if (!chunkBukkitValues.isEmpty()) extraTag.put("ChunkBukkitValues", chunkBukkitValues); - // Find first non-null chunk section. If all sections are null, chunk is empty so return null - return Arrays.stream(sectionArray) - .filter(Objects::nonNull) - .findFirst() - .map(x -> new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMaps, tileEntities, entities, extraTag, null, null, null, null)) //TODO: Convert poi, block and fluid - .orElse(null); + //If all sections are null, chunk is empty so return null + if(!hasSection) return null; + + return new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMaps, tileEntities, entities, extraTag, null, null, null, null); //TODO: Convert poi, block and fluid } private static T applyByteArrayOrNull(final CompoundBinaryTag tag, final String key, final Function transform) { diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java index 3a807650..a11791c7 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java @@ -97,7 +97,7 @@ static byte[] serializeChunks(SlimeWorld world, Collection chunks, E outStream.writeInt(chunk.getZ()); // Chunk sections - SlimeChunkSection[] sections = Arrays.stream(chunk.getSections()).filter(Objects::nonNull).toList().toArray(new SlimeChunkSection[0]); + SlimeChunkSection[] sections = chunk.getSections(); outStream.writeInt(sections.length); for (SlimeChunkSection slimeChunkSection : sections) { diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java index a33fd864..c8d36651 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java @@ -131,10 +131,9 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope // Chunk Sections { // See WorldUtils - int sectionAmount = slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MAX) - slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MIN) + 1; - SlimeChunkSection[] chunkSectionArray = new SlimeChunkSection[sectionAmount]; - int sectionCount = chunkData.readInt(); + SlimeChunkSection[] chunkSectionArray = new SlimeChunkSection[sectionCount]; + for (int sectionId = 0; sectionId < sectionCount; sectionId++) { // Block Light Nibble Array NibbleArray blockLightArray; diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java index d5480903..fcb25b70 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java @@ -71,10 +71,9 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope int z = chunkData.readInt(); // Sections - int sectionAmount = slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MAX) - slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MIN) + 1; - SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionAmount]; - int sectionCount = chunkData.readInt(); + SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionCount]; + for (int sectionId = 0; sectionId < sectionCount; sectionId++) { // Block Light Nibble Array diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java index 2593f5f4..71516aa2 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java @@ -70,10 +70,9 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope int z = chunkData.readInt(); // Sections - int sectionAmount = slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MAX) - slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MIN) + 1; - SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionAmount]; - int sectionCount = chunkData.readInt(); + SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionCount]; + for (int sectionId = 0; sectionId < sectionCount; sectionId++) { // Block Light Nibble Array diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13SlimeWorldDeSerializer.java index d4e4e715..38cbac88 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13SlimeWorldDeSerializer.java @@ -68,10 +68,8 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope int z = chunkData.readInt(); // Sections - int sectionAmount = slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MAX) - slimePropertyMap.getValue(SlimeProperties.CHUNK_SECTION_MIN) + 1; - SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionAmount]; - int sectionCount = chunkData.readInt(); + SlimeChunkSection[] chunkSections = new SlimeChunkSection[sectionCount]; for (int sectionId = 0; sectionId < sectionCount; sectionId++) { byte sectionFlags = chunkData.readByte(); diff --git a/gradle.properties b/gradle.properties index 7531262f..c4f2167d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,12 @@ group=com.infernalsuite.asp -apiVersion=4.1.0 -version=1.21.8-R0.1-SNAPSHOT +apiVersion=4.2.0-SNAPSHOT +version=1.21.10-R0.1-SNAPSHOT +mcVersion=1.21.10 -mcVersion=1.21.8 -paperRef=29c8822d90899c89d2689338e81a98f690bcba12 +paperRef=f8cf03d68ccd498cc7c5be434f1269ead112aaf4 org.gradle.caching=true org.gradle.parallel=true org.gradle.vfs.watch=false -org.gradle.jvmargs=-Xmx10g \ No newline at end of file +org.gradle.jvmargs=-Xmx10g diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fbcd35b2..5d4eab7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,9 +15,9 @@ lettuce = "6.5.1.RELEASE" lombok = "1.18.36" lombok-plugin = "8.11" mongo = "5.2.1" -paperweight = "2.0.0-beta.17" +paperweight = "2.0.0-beta.19" plugin-yml-paper = "0.6.0" -shadow = "8.3.5" +shadow = "9.2.2" slf4j = "2.0.16" zstd = "1.5.6-8" @@ -50,4 +50,4 @@ lettuce = { module = "io.lettuce:lettuce-core", version.ref = "lettuce" } lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } mongo = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } -zstd = { module = "com.github.luben:zstd-jni", version.ref = "zstd" } \ No newline at end of file +zstd = { module = "com.github.luben:zstd-jni", version.ref = "zstd" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867c..bad7c246 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index ccd12612..d38dbeaf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,6 @@ plugins { rootProject.name = "ASPaper" -include(":gradle:platform") include(":api") include(":core") include(":importer")