diff --git a/SLIME_FORMAT b/SLIME_FORMAT index 3360d2e4..53df685a 100644 --- a/SLIME_FORMAT +++ b/SLIME_FORMAT @@ -1,9 +1,10 @@ ------------------------------------- -“Slime” file format +“Slime” file format (Version 13) 2 bytes - magic = 0xB10B -1 byte (ubyte) - version, current = 0x0C +1 byte (ubyte) - version, current = 0x0D 4 bytes (int) - world version +1 byte (ubyte) - additional world flags (bitmask - see options below) 4 bytes (int) - compressed chunks size 4 bytes (int) - uncompressed chunks size (size determined from bitmask) @@ -13,6 +14,11 @@ 4 bytes (int) - uncompressed “extra” size [depends] - extra compound tag compressed using zstd (used for PDC, and/or custom data) +------------------------------------- +Additional world flag index +POI_CHUNKS - Enum ordinal 0 - Bitmask value 1 +FLUID_TICKS - Enum ordinal 1 - Bitmask value 2 +BLOCK_TICKS - Enum ordinal 2 - Bitmask value 4 ------------------------------------- Custom chunk format @@ -20,28 +26,50 @@ Custom chunk format 4 byte (int) - chunk z 4 bytes (int) section count [for each section] - 1 byte (boolean) - has sky light + 1 byte (ubyte) - section flags (Bitmask value 1 = has block light, 2 = has sky light) [if has sky light] 2048 bytes - sky light - 1 byte (boolean) - has block light - [if has block light] - 2048 bytes - block light + [if has block light] + 2048 bytes - block light 4 bytes (int) - block states byte size - + + same format as mc 4 bytes (int) - biomes byte size - + + same format as mc 4 bytes (int) - heightmaps size - + same format as mc, uncompressed +[if poi chunks flag is enabled] + 4 bytes (int) - poi chunk size + + "Sections" tag of the mc poi chunk compound + uncompressed +[if block ticks flag is enabled] + 4 bytes (int) - fluid ticks size + + Same format as mc + inside an nbt list named "block_ticks" + uncompressed +[if fluid ticks flag is enabled] + 4 bytes (int) - heightmaps size + + Same format as mc + inside an nbt list named "fluid_ticks" + uncompressed +[for every flag set that does not exist yet, for backward compatibility of new future flags] + 4 bytes (int) - size + + 4 bytes (int) - tile entities size - + Same format as mc - inside an nbt list named “tileEntities”, in a global compound + inside an nbt list named “tileEntities” uncompressed 4 bytes (int) entities size - - Same format as mc EXCEPT optional “CustomId” - inside an nbt list named “entities”, in a global compound + + Same format as mc + inside an nbt list named “entities” uncompressed [depends] - compound tag uncompressed (used for PDC, and/or custom data @@ -60,3 +88,4 @@ Version history: - v10: Use minecraft version id, remove legacy version artifacts - v11: Move entities and tile entities into the chunk structure - v12: Add support for chunk-based PDC + - v13: Add support for additional world data diff --git a/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java b/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java index 4ada699f..3a714628 100644 --- a/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java +++ b/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java @@ -9,5 +9,5 @@ public class SlimeFormat { public static final byte[] SLIME_HEADER = new byte[] { -79, 11 }; /** Latest version of the SRF that SWM supports **/ - public static final byte SLIME_VERSION = 12; + public static final byte SLIME_VERSION = 13; } diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java index b6d3a0b4..acc37b26 100644 --- a/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java @@ -1,7 +1,9 @@ package com.infernalsuite.asp.api.world; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import java.util.List; import java.util.Map; @@ -38,6 +40,7 @@ public interface SlimeChunk { * * @return A {@link CompoundBinaryTag} containing all the height maps of the chunk. */ + @Nullable CompoundBinaryTag getHeightMaps(); /** @@ -73,4 +76,41 @@ public interface SlimeChunk { */ @Nullable CompoundBinaryTag getUpgradeData(); + + /** + * Returns all block ticks of the chunk. This data is only saved when {@link SlimeProperties#SAVE_BLOCK_TICKS} is true. + *

+ * Even if {@link SlimeProperties#SAVE_BLOCK_TICKS} is false, this might still return + * a {@link ListBinaryTag} containing the block ticks, if the data was present when the world + * was read or when the chunk is currently loaded. + * + * @return A {@link ListBinaryTag} containing all the block ticks of the chunk, if present. + * @see SlimeProperties#SAVE_FLUID_TICKS + */ + @Nullable + ListBinaryTag getBlockTicks(); + + /** + * Returns all fluid ticks of the chunk. This data is only saved when {@link SlimeProperties#SAVE_FLUID_TICKS} is true. + *

+ * Even if {@link SlimeProperties#SAVE_FLUID_TICKS} is false, this might still return a {@link ListBinaryTag} + * containing the fluid ticks, if the data was present when the world was read or when the chunk is currently loaded. + * + * @return A {@link ListBinaryTag} containing all the fluid ticks of the chunk, if present. + * @see SlimeProperties#SAVE_FLUID_TICKS + */ + @Nullable + ListBinaryTag getFluidTicks(); + + /** + * Returns the poi sections of the chunk. This data is only saved when {@link SlimeProperties#SAVE_POI} is true. + *

+ * Even if {@link SlimeProperties#SAVE_POI} is false, this might still return a {@link CompoundBinaryTag} + * containing the poi sections, if the data was present when the world was read or when the chunk is currently loaded. + * + * @return A {@link CompoundBinaryTag} containing the poi chunks of the chunk, if present. + * @see SlimeProperties#SAVE_POI + */ + @Nullable + CompoundBinaryTag getPoiChunkSections(); } 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 90a56d53..92c4ac1c 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 @@ -98,12 +98,39 @@ public class SlimeProperties { value.equalsIgnoreCase("aggressive") || value.equalsIgnoreCase("never") ); - - @ApiStatus.Experimental public static final SlimePropertyInt CHUNK_SECTION_MIN = SlimePropertyInt.create("chunkSectionMin", -4); @ApiStatus.Experimental public static final SlimePropertyInt CHUNK_SECTION_MAX = SlimePropertyInt.create("chunkSectionMax", 19); + /** + * The sea level to use for the slime world. This affects mob spawning for water specific mobs like squids or turtles. + * To achieve the default vanilla behaviour, change this value to 63 instead of the default of -63. + */ + @ApiStatus.Experimental + public static final SlimePropertyInt SEA_LEVEL = SlimePropertyInt.create("seaLevel", -63); + /** + * Whether to save the POI data in the world or not. Worlds that already have POI data will still load the data + * even if this is set to false. However, the POI data will no longer be saved when the world is saved. + *

+ * POI data is e.g. used for villager jobs, villager beds, bees, lightning rods. This data is not saved in slime by default. + */ + public static final SlimePropertyBoolean SAVE_POI = SlimePropertyBoolean.create("savePOI", false); + + /** + * Whether to save the block ticks in the world or not. Worlds that already have block tick data will still load the data + * even if this is set to false. However, the block tick data will no longer be saved when the world is saved. + *

+ * Block ticks are used for scheduling e.g. redstone updates. If this data is not saved, redstone will pause when chunks or worlds get re-loaded. + */ + public static final SlimePropertyBoolean SAVE_BLOCK_TICKS = SlimePropertyBoolean.create("saveBlockTicks", false); + + /** + * Whether to save the fluid ticks in the world or not. Worlds that already have fluid tick data will still load the data + * even if this is set to false. However, the fluid tick data will no longer be saved when the world is saved. + *

+ * Fluid ticks are used for scheduling liquid flow updates. If this data is not saved, fluids will freeze when chunks or worlds get re-loaded. + */ + public static final SlimePropertyBoolean SAVE_FLUID_TICKS = SlimePropertyBoolean.create("saveFluidTicks", false); } diff --git a/aspaper-api/build.gradle.kts.patch b/aspaper-api/build.gradle.kts.patch index 33a97d1b..69ef7241 100644 --- a/aspaper-api/build.gradle.kts.patch +++ b/aspaper-api/build.gradle.kts.patch @@ -1,27 +1,26 @@ --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts -@@ -39,7 +_,7 @@ +@@ -40,6 +_,7 @@ } dependencies { -- + api(project(":api")) //ASP // 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") -@@ -93,7 +_,7 @@ +@@ -90,7 +_,7 @@ testRuntimeOnly("org.junit.platform:junit-platform-launcher") } --val generatedApiPath: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() -+val generatedApiPath: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() +-val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() ++val generatedDir: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() idea { module { - generatedSourceDirs.add(generatedApiPath.toFile()) -@@ -103,6 +_,18 @@ + generatedSourceDirs.add(generatedDir.toFile()) +@@ -100,6 +_,18 @@ main { java { - srcDir(generatedApiPath) + srcDir(generatedDir) + srcDir(file("../paper-api/src/main/java")) + } + resources { @@ -37,7 +36,7 @@ } } } -@@ -169,7 +_,7 @@ +@@ -166,7 +_,7 @@ tasks.withType { val options = options as StandardJavadocDocletOptions @@ -46,7 +45,7 @@ options.use() options.isDocFilesSubDirs = true options.links( -@@ -202,11 +_,11 @@ +@@ -199,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 35b337a9..eeddd777 100644 --- a/aspaper-server/build.gradle.kts.patch +++ b/aspaper-server/build.gradle.kts.patch @@ -1,8 +1,8 @@ --- a/paper-server/build.gradle.kts +++ b/paper-server/build.gradle.kts -@@ -21,6 +_,17 @@ - // macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java") - // gitFilePatches = true +@@ -26,6 +_,17 @@ + minecraftVersion = providers.gradleProperty("mcVersion") + gitFilePatches = false + val aspaper = forks.register("aspaper") { + upstream.patchDir("paperServer") { @@ -16,9 +16,9 @@ + activeFork = aspaper + spigot { - buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42" - packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment -@@ -101,7 +_,20 @@ + enabled = true + buildDataRef = "436eac9815c211be1a2a6ca0702615f995e81c44" +@@ -107,7 +_,19 @@ } } @@ -33,25 +33,23 @@ + resources { srcDir("../paper-server/src/test/resources") } + } +} -+ +val log4jPlugins = sourceSets.create("log4jPlugins") { + java { srcDir("../paper-server/src/log4jPlugins/java") } +} configurations.named(log4jPlugins.compileClasspathConfigurationName) { extendsFrom(configurations.compileClasspath.get()) } -@@ -119,7 +_,9 @@ +@@ -129,7 +_,8 @@ } dependencies { - implementation(project(":paper-api")) + implementation(project(":aspaper-api")) //ASP + implementation(project(":core")) //ASP -+ implementation("commons-io:commons-io:2.11.0") implementation("ca.spottedleaf:concurrentutil:0.0.3") 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 -@@ -189,14 +_,14 @@ +@@ -205,14 +_,14 @@ val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", @@ -71,3 +69,12 @@ "Build-Number" to (build ?: ""), "Build-Time" to buildTime.toString(), "Git-Branch" to gitBranch, +@@ -271,7 +_,7 @@ + jvmArgumentProviders.add(provider) + } + +-val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() ++val generatedDir: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-server/src/generated/java").asFile.toPath() + idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) 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 a4897952..aa90d7f5 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 c5ddf6c0f0ff795da2f7aec8915a081b334423ec..a373f5b8e03f4179a4d9f63d79abc19a38f952b6 100644 +index 53ec0565a76663de74bb228c23c3d74a640a20f9..2c85ace929aeb9078fe868bcc56a2be08ac2f7ba 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -689,7 +689,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -684,7 +684,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/0002-World-overrides.patch b/aspaper-server/minecraft-patches/features/0002-World-overrides.patch index d7db4aa7..e520cc59 100644 --- a/aspaper-server/minecraft-patches/features/0002-World-overrides.patch +++ b/aspaper-server/minecraft-patches/features/0002-World-overrides.patch @@ -5,7 +5,7 @@ Subject: [PATCH] World overrides diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 1509c96ae9247d1d5ddd0fc50210f88da10bf3de..bbc4be3c3811ee19bd791cb6626474038d35a298 100644 +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 this.pvpMode = server.isPvpAllowed(); this.levelStorageAccess = levelStorageAccess; -- this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()); -+ this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()) : UUID.randomUUID(); //ASP - avoid IO calls +- 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 // CraftBukkit end this.tickTime = tickTime; this.server = server; diff --git a/aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch b/aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch index 8d003194..b9dad947 100644 --- a/aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch +++ b/aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Prevent config disk io on world load diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 60608cea4e564c1ce47f8a4de2c1a48986bbdecb..1b5d65136421b63353b1c6cd8ae5d413ec070b92 100644 +index 939605ce630ab66b7236962dec3ae61ed23dc222..51cfeb87385b7e40922049a197a417a3892c2fce 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -614,7 +614,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -609,7 +609,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ) { //ASP end // CraftBukkit start @@ -16,17 +16,17 @@ index 60608cea4e564c1ce47f8a4de2c1a48986bbdecb..1b5d65136421b63353b1c6cd8ae5d413 + 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(); this.levelStorageAccess = levelStorageAccess; - this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile()) : UUID.randomUUID(); //ASP - avoid IO calls + this.uuid = bootstrap == null ? org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile()) : java.util.UUID.randomUUID(); //ASP - avoid IO calls diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index 1dbe7c7c1051c3972105534a07ce50d4cf98fc85..e1d3c292b9efccb032245f4f1618f2650f0bc619 100644 +index e4b9a564aad3d9b673808caa18265b06592ceab8..8bb30eba86b74f155ef9580dc6c01d761e23dd5a 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -851,7 +851,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -838,7 +838,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl this.maxSectionY = this.maxY >> 4; this.sectionsCount = this.maxSectionY - this.minSectionY + 1; // Paper end - getblock optimisations - cache world height/sections - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), !(this instanceof com.infernalsuite.asp.level.SlimeLevelInstance)); // Spigot //ASP - Improve Slime IO this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config - this.generator = gen; - this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); + this.generator = generator; + this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment); diff --git a/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch b/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch index bd3e2ff8..f1279fa1 100644 --- a/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch +++ b/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch @@ -5,21 +5,21 @@ Subject: [PATCH] Read only dimension data store diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 6540b2d6a1062d883811ce240c49d30d1925b291..f89eb4e909c2eeb22732dcb368b3758637f036f7 100644 +index 5d63bf024cbcbd2f627c64fee77553c9a512bd15..da169d319e9c09de2d5a34784af4e4c133b10f46 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -207,7 +207,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon +@@ -210,7 +210,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon LOGGER.error("Failed to create dimension data storage directory", (Throwable)var15); } -- this.dataStorage = new DimensionDataStorage(path, fixerUpper, level.registryAccess()); +- this.dataStorage = new DimensionDataStorage(new SavedData.Context(level), path, fixerUpper, level.registryAccess()); + //ASP start - No dimension data storage + if(level instanceof com.infernalsuite.asp.level.SlimeLevelInstance) { -+ this.dataStorage = new com.infernalsuite.asp.level.ReadOnlyDimensionDataStorage(path, fixerUpper, level.registryAccess()); ++ this.dataStorage = new com.infernalsuite.asp.level.ReadOnlyDimensionDataStorage(new SavedData.Context(level), path, fixerUpper, level.registryAccess()); + } else { -+ this.dataStorage = new DimensionDataStorage(path, fixerUpper, level.registryAccess()); ++ this.dataStorage = new DimensionDataStorage(new SavedData.Context(level), path, fixerUpper, level.registryAccess()); + } + //ASP end - No dimension data storage + this.ticketStorage = this.dataStorage.computeIfAbsent(TicketStorage.TYPE); this.chunkMap = new ChunkMap( level, - levelStorageAccess, 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 new file mode 100644 index 00000000..68303fee --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch @@ -0,0 +1,10 @@ +--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java ++++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java +@@ -206,6 +_,7 @@ + + @Override + public boolean forceNoSave(final ChunkAccess chunk) { ++ if(chunk instanceof LevelChunk levelChunk && levelChunk.level instanceof com.infernalsuite.asp.level.SlimeLevelInstance) return true; //ASP - prevent normal saving + return chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave; + } + 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 cfd198c0..8579614f 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 -@@ -184,7 +_,9 @@ +@@ -188,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 ff3cc89a..d5af1d38 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,31 +1,13 @@ --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -@@ -116,7 +_,7 @@ - } - - if (!transientChunk) { -- if (entityChunk != null) { -+ if (!(this.world instanceof com.infernalsuite.asp.level.SlimeLevelInstance) && entityChunk != null) { - final List entities = ChunkEntitySlices.readEntities(this.world, entityChunk); - - ((ChunkSystemServerLevel)this.world).moonrise$getEntityLookup().addEntityChunkEntities(entities, new ChunkPos(this.chunkX, this.chunkZ)); -@@ -894,7 +_,7 @@ - final ChunkEntitySlices entityChunk = state.entityChunk(); - final PoiChunk poiChunk = state.poiChunk(); - -- final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk); -+ final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk) || world instanceof com.infernalsuite.asp.level.SlimeLevelInstance; //ASP - prevent saving - - // unload chunk data - if (chunk != null) { -@@ -910,13 +_,24 @@ +@@ -912,13 +_,24 @@ } if (chunk instanceof LevelChunk levelChunk) { + //ASP start - unloadStage1 sets the entitySlices to null, so we can't get them otherwise unfortunately. + //This needs to be above world.unload since world.unload removes block entities + if(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance instance) { -+ instance.unload(levelChunk, entityChunk); ++ instance.unload(levelChunk, entityChunk, poiChunk); + } + //ASP end this.world.unload(levelChunk); @@ -44,3 +26,16 @@ // yes this is a hack to pass the compound tag through... final CompoundTag lastEntityUnload = this.lastEntityUnload; this.lastEntityUnload = null; +@@ -1717,6 +_,12 @@ + boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved())); + boolean canSavePOI = !forceNoSaveChunk && (poi != null && poi.isDirty()); + boolean canSaveEntities = entities != null; ++ ++ //ASP start - prevent saving ++ if(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance) { ++ canSaveEntities = false; ++ } ++ //ASP end - prevent saving + + if (canSaveChunk) { + canSaveChunk = this.saveChunk(chunk, false); 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 44ce62a2..ed0f1226 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 -@@ -570,7 +_,32 @@ +@@ -565,7 +_,32 @@ } // Paper end - chunk tick iteration @@ -34,7 +34,7 @@ MinecraftServer server, Executor dispatcher, LevelStorageSource.LevelStorageAccess levelStorageAccess, -@@ -587,6 +_,7 @@ +@@ -582,6 +_,7 @@ org.bukkit.generator.ChunkGenerator gen, // CraftBukkit org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit ) { @@ -42,7 +42,7 @@ // 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(); -@@ -614,6 +_,13 @@ +@@ -609,6 +_,13 @@ chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); } // CraftBukkit end @@ -56,7 +56,7 @@ boolean flag = server.forceSynchronousWrites(); DataFixer fixerUpper = server.getFixerUpper(); // Paper - rewrite chunk system -@@ -695,6 +_,12 @@ +@@ -689,6 +_,12 @@ public void setDragonFight(@Nullable EndDragonFight dragonFight) { this.dragonFight = dragonFight; } diff --git a/aspaper-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch b/aspaper-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch deleted file mode 100644 index 81744efd..00000000 --- a/aspaper-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -387,6 +_,12 @@ - if (flag2) { - lightEngine.queueSectionData(LightLayer.SKY, sectionPos, sectionData.skyLight); - } -+ -+ //ASP start -+ if (level instanceof com.infernalsuite.asp.level.SlimeLevelInstance) { -+ poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection); -+ } -+ //ASP end - } - } - diff --git a/aspaper-server/paper-patches/features/0003-Branding.patch b/aspaper-server/paper-patches/features/0003-Branding.patch index bddf7be5..2a99d901 100644 --- a/aspaper-server/paper-patches/features/0003-Branding.patch +++ b/aspaper-server/paper-patches/features/0003-Branding.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Branding diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -index 532306cacd52579cdf37e4aca25887b1ed3ba6a1..eec04fcdd4b6c6f92a2db37bfe06dca2a93e300d 100644 +index d0554ed6631535815e5932930911e3fe1dee8710..ac171c6b9008eaaeb9e433fdb188bd797fd95759 100644 --- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java @@ -35,7 +35,7 @@ public class PaperVersionFetcher implements VersionFetcher { @@ -56,7 +56,7 @@ index 532306cacd52579cdf37e4aca25887b1ed3ba6a1..eec04fcdd4b6c6f92a2db37bfe06dca2 .hoverEvent(text("Click to open", NamedTextColor.WHITE)) .clickEvent(ClickEvent.openUrl(DOWNLOAD_PAGE)))); diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java -index 790bad0494454ca12ee152e3de6da3da634d9b20..d047646990ff52b31a24bdebf000ecfbebdae99a 100644 +index 74ffdc823e66fc5ec027c4b7c462382bcbfe2be2..2610e8d1ce20331895b0a76fa50efc062001678e 100644 --- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java @@ -31,6 +31,7 @@ public record ServerBuildInfoImpl( @@ -76,8 +76,8 @@ index 790bad0494454ca12ee152e3de6da3da634d9b20..d047646990ff52b31a24bdebf000ecfb getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) - .orElse(BRAND_PAPER_NAME), + .orElse(BRAND_ADVANCED_SLIME_PAPER_NAME), - SharedConstants.getCurrentVersion().getId(), - SharedConstants.getCurrentVersion().getName(), + SharedConstants.getCurrentVersion().id(), + SharedConstants.getCurrentVersion().name(), getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) @@ -61,7 +62,7 @@ public record ServerBuildInfoImpl( @@ -89,7 +89,7 @@ index 790bad0494454ca12ee152e3de6da3da634d9b20..d047646990ff52b31a24bdebf000ecfb @Override diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 774556a62eb240da42e84db4502e2ed43495be17..ee1ffebaf4e9f7e2f8e0fd8406357b64d38b7399 100644 +index 774556a62eb240da42e84db4502e2ed43495be17..0c54eb1187c31a2e6efd4b0898fc600fa1bddeee 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java @@ -11,7 +11,7 @@ public final class Versioning { 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 ff237d48..c81a4ed9 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 090b295ccd7f61c2f1cc5131ffb0371c22e48f04..0bbc51dcf0ba7773b818994890d14fd0300608f3 100644 +index 09096a99c914b9ffe555e876559b02c437543f58..4ac6cc136824e43ac8a8677a1c45735a669882af 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1518,6 +1518,12 @@ public final class CraftServer implements Server { +@@ -1457,6 +1457,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/0005-Prevent-config-disk-io-on-world-load.patch b/aspaper-server/paper-patches/features/0005-Prevent-config-disk-io-on-world-load.patch index 70c5ff9d..43df3051 100644 --- a/aspaper-server/paper-patches/features/0005-Prevent-config-disk-io-on-world-load.patch +++ b/aspaper-server/paper-patches/features/0005-Prevent-config-disk-io-on-world-load.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Prevent config disk io on world load diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java -index e0d4222a99f22d7130d95cf29b034a98f2f3b76e..776440ec20c7bf0acac8678773b0a5d29548c270 100644 +index c935d75b83338ce41507e9be11153dd1c4052cb6..3feaba566c22b1e93b2b4edc7786723a2f9e28c6 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java -@@ -78,7 +78,12 @@ public class SpigotConfig { +@@ -79,7 +79,12 @@ public class SpigotConfig { } } + // ASP start - Improve Slime IO - public static void readConfig(Class clazz, Object instance) { // Paper - package-private -> public + public static void readConfig(Class clazz, Object instance) { + readConfig(clazz, instance, true); + } + public static void readConfig(Class clazz, Object instance, boolean save) { @@ -21,7 +21,7 @@ index e0d4222a99f22d7130d95cf29b034a98f2f3b76e..776440ec20c7bf0acac8678773b0a5d2 for (Method method : clazz.getDeclaredMethods()) { if (Modifier.isPrivate(method.getModifiers())) { if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { -@@ -95,7 +100,11 @@ public class SpigotConfig { +@@ -96,7 +101,11 @@ public class SpigotConfig { } try { @@ -35,7 +35,7 @@ index e0d4222a99f22d7130d95cf29b034a98f2f3b76e..776440ec20c7bf0acac8678773b0a5d2 Bukkit.getLogger().log(Level.SEVERE, "Could not save " + SpigotConfig.CONFIG_FILE, ex); } diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java -index 89e2adbc1e1a0709d03e151e3ffcdbff10a44098..6b2241efbc248324f178a547aea9f398ed624247 100644 +index 43c6240ec2855c0f668ce04de29d22a223d2612f..c6f398977149c8b35454fb79f099322c36d557d5 100644 --- a/src/main/java/org/spigotmc/SpigotWorldConfig.java +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -10,17 +10,28 @@ public class SpigotWorldConfig { diff --git a/aspaper-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch b/aspaper-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch new file mode 100644 index 00000000..7464027d --- /dev/null +++ b/aspaper-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch @@ -0,0 +1,26 @@ +--- a/src/main/java/com/destroystokyo/paper/Metrics.java ++++ b/src/main/java/com/destroystokyo/paper/Metrics.java +@@ -593,7 +_,7 @@ + boolean logFailedRequests = config.getBoolean("logFailedRequests", false); + // Only start Metrics, if it's enabled in the config + if (config.getBoolean("enabled", true)) { +- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); ++ Metrics metrics = new Metrics("Advanced Slime Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); //ASP + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); +@@ -603,6 +_,14 @@ + + metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); ++ //ASP start - add slime plugin ++ metrics.addCustomChart(new Metrics.SimplePie("slime_plugin_installed", () -> { ++ Plugin slimeWorldPlugin = Bukkit.getPluginManager().getPlugin("SlimeWorldPlugin"); ++ ++ return slimeWorldPlugin != null ? slimeWorldPlugin.getPluginMeta().getVersion() : "not installed"; ++ })); ++ //ASP end - add slime plugin ++ + final String paperVersion; + final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); + if (implVersion != null) { 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 94ef404f..015feb95 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 -@@ -1508,6 +_,8 @@ +@@ -1447,6 +_,8 @@ return false; } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java index 848a6e1c..27c0f2b6 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java @@ -1,21 +1,7 @@ package com.infernalsuite.asp; import com.infernalsuite.asp.api.utils.NibbleArray; -import net.kyori.adventure.nbt.BinaryTag; -import net.kyori.adventure.nbt.ByteArrayBinaryTag; -import net.kyori.adventure.nbt.ByteBinaryTag; -import net.kyori.adventure.nbt.CompoundBinaryTag; -import net.kyori.adventure.nbt.DoubleBinaryTag; -import net.kyori.adventure.nbt.EndBinaryTag; -import net.kyori.adventure.nbt.FloatBinaryTag; -import net.kyori.adventure.nbt.IntArrayBinaryTag; -import net.kyori.adventure.nbt.IntBinaryTag; -import net.kyori.adventure.nbt.ListBinaryTag; -import net.kyori.adventure.nbt.LongArrayBinaryTag; -import net.kyori.adventure.nbt.LongBinaryTag; -import net.kyori.adventure.nbt.ShortBinaryTag; -import net.kyori.adventure.nbt.StringBinaryTag; -import net.kyori.adventure.nbt.TagStringIO; +import net.kyori.adventure.nbt.*; import net.minecraft.nbt.ByteArrayTag; import net.minecraft.nbt.ByteTag; import net.minecraft.nbt.CompoundTag; @@ -37,6 +23,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; public class Converter { @@ -99,27 +86,39 @@ public static Tag convertTag(T tag) { public static T convertTag(Tag base) { return switch (base.getId()) { case Tag.TAG_END -> (T) EndBinaryTag.endBinaryTag(); - case Tag.TAG_BYTE -> (T) ByteBinaryTag.byteBinaryTag(((ByteTag) base).getAsByte()); - case Tag.TAG_SHORT -> (T) ShortBinaryTag.shortBinaryTag(((ShortTag) base).getAsShort()); - case Tag.TAG_INT -> (T) IntBinaryTag.intBinaryTag(((IntTag) base).getAsInt()); - case Tag.TAG_LONG -> (T) LongBinaryTag.longBinaryTag(((LongTag) base).getAsLong()); - case Tag.TAG_FLOAT -> (T) FloatBinaryTag.floatBinaryTag(((FloatTag) base).getAsFloat()); - case Tag.TAG_DOUBLE -> (T) DoubleBinaryTag.doubleBinaryTag(((DoubleTag) base).getAsDouble()); + case Tag.TAG_BYTE -> (T) ByteBinaryTag.byteBinaryTag(((ByteTag) base).byteValue()); + case Tag.TAG_SHORT -> (T) ShortBinaryTag.shortBinaryTag(((ShortTag) base).shortValue()); + case Tag.TAG_INT -> (T) IntBinaryTag.intBinaryTag(((IntTag) base).intValue()); + case Tag.TAG_LONG -> (T) LongBinaryTag.longBinaryTag(((LongTag) base).longValue()); + case Tag.TAG_FLOAT -> (T) FloatBinaryTag.floatBinaryTag(((FloatTag) base).floatValue()); + case Tag.TAG_DOUBLE -> (T) DoubleBinaryTag.doubleBinaryTag(((DoubleTag) base).doubleValue()); case Tag.TAG_BYTE_ARRAY -> (T) ByteArrayBinaryTag.byteArrayBinaryTag(((ByteArrayTag) base).getAsByteArray()); - case Tag.TAG_STRING -> (T) StringBinaryTag.stringBinaryTag(((StringTag) base).getAsString()); + case Tag.TAG_STRING -> (T) StringBinaryTag.stringBinaryTag(base.asString().orElseThrow()); //TODO(david): Figure out what to do with optional? case Tag.TAG_LIST -> { ListTag originalList = ((ListTag) base); if(originalList.isEmpty()) { yield (T) ListBinaryTag.empty(); } List list = new ArrayList<>(originalList.size()); - for (Tag entry : originalList) list.add(convertTag(entry)); - yield (T) ListBinaryTag.listBinaryTag(list.getFirst().type(), list); + + BinaryTagType tagType = null; + for (Tag entry : originalList) { + BinaryTag converted = convertTag(entry); + + if(tagType != null && !converted.type().equals(tagType)) { + tagType = BinaryTagTypes.LIST_WILDCARD; + } else if(tagType == null) { + tagType = converted.type(); + } + + list.add(converted); + } + yield (T) ListBinaryTag.listBinaryTag(tagType, list); } case Tag.TAG_COMPOUND -> { CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); CompoundTag originalCompound = ((CompoundTag) base); - for (String key : originalCompound.getAllKeys()) builder.put(key, convertTag(Objects.requireNonNull(originalCompound.get(key)))); + for (String key : originalCompound.keySet()) builder.put(key, convertTag(Objects.requireNonNull(originalCompound.get(key)))); yield (T) builder.build(); } case Tag.TAG_INT_ARRAY -> (T) IntArrayBinaryTag.intArrayBinaryTag(((IntArrayTag) base).getAsIntArray()); @@ -127,5 +126,4 @@ public static T convertTag(Tag base) { default -> throw new IllegalArgumentException("Invalid tag type " + base.getId()); }; } - } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java index d1b255e5..b309eeba 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java @@ -1,13 +1,14 @@ package com.infernalsuite.asp; import ca.spottedleaf.dataconverter.converters.DataConverter; -import ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType; import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType; import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; import ca.spottedleaf.dataconverter.types.MapType; import ca.spottedleaf.dataconverter.types.nbt.NBTListType; import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; import com.infernalsuite.asp.api.SlimeDataConverter; +import com.infernalsuite.asp.level.SlimeChunkConverter; import com.infernalsuite.asp.serialization.SlimeWorldReader; import com.infernalsuite.asp.skeleton.SkeletonSlimeWorld; import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; @@ -19,13 +20,10 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.kyori.adventure.nbt.ListBinaryTag; -import net.kyori.adventure.nbt.TagStringIO; import net.minecraft.SharedConstants; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -34,7 +32,7 @@ class SimpleDataFixerConverter implements SlimeWorldReader, SlimeDat @Override public SlimeWorld readFromData(SlimeWorld data) { - int newVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + int newVersion = SharedConstants.getCurrentVersion().dataVersion().version(); int currentVersion = data.getDataVersion(); // Already fixed if (currentVersion == newVersion) { @@ -79,8 +77,10 @@ public SlimeWorld readFromData(SlimeWorld data) { dataSection.getBlockLight(), dataSection.getSkyLight() ); - } + + CompoundBinaryTag newPoi = chunk.getPoiChunkSections() != null ? convertPoiSections(chunk.getPoiChunkSections(), currentVersion, encodedCurrentVersion, encodedNewVersion) : null; + chunks.put(chunkPos, new SlimeChunkSkeleton( chunk.getX(), chunk.getZ(), @@ -89,7 +89,10 @@ public SlimeWorld readFromData(SlimeWorld data) { blockEntities, entities, chunk.getExtraData(), - chunk.getUpgradeData() + chunk.getUpgradeData(), + newPoi, + chunk.getBlockTicks(), + chunk.getFluidTicks() )); } @@ -105,6 +108,12 @@ public SlimeWorld readFromData(SlimeWorld data) { ); } + private CompoundBinaryTag convertPoiSections(CompoundBinaryTag poiChunkSections, int currentVersion, long encodedCurrentVersion, long encodedNewVersion) { + CompoundTag poiChunk = SlimeChunkConverter.createPoiChunkFromSlimeSections(poiChunkSections, currentVersion); + MCTypeRegistry.ENTITY.convert(new NBTMapType(poiChunk), encodedCurrentVersion, encodedNewVersion); + return SlimeChunkConverter.getSlimeSectionsFromPoiCompound(poiChunk); + } + @Override public SlimeWorld applyDataFixers(SlimeWorld world) { return readFromData(world); @@ -123,7 +132,7 @@ private static CompoundBinaryTag convertAndBack(CompoundBinaryTag value, Consume public CompoundBinaryTag convertChunkTo1_13(CompoundBinaryTag tag) { CompoundTag nmsTag = (CompoundTag) Converter.convertTag(tag); - int version = nmsTag.getInt("DataVersion"); + int version = nmsTag.getInt("DataVersion").orElseThrow(); long encodedNewVersion = DataConverter.encodeVersions(1631, Integer.MAX_VALUE); long encodedCurrentVersion = DataConverter.encodeVersions(version, Integer.MAX_VALUE); @@ -165,7 +174,6 @@ public List convertTileEntities(List input @Override public ListBinaryTag convertBlockPalette(ListBinaryTag input, int from, int to) { - long encodedNewVersion = DataConverter.encodeVersions(to, Integer.MAX_VALUE); long encodedCurrentVersion = DataConverter.encodeVersions(from, Integer.MAX_VALUE); @@ -173,7 +181,7 @@ public ListBinaryTag convertBlockPalette(ListBinaryTag input, int from, int to) NBTListType listType = new NBTListType(nbtList); for (int i = 0, len = listType.size(); i < len; ++i) { - final MapType replace = MCTypeRegistry.BLOCK_STATE.convert(listType.getMap(i), + final MapType replace = MCTypeRegistry.BLOCK_STATE.convert(listType.getMap(i), encodedCurrentVersion, encodedNewVersion); if (replace != null) { listType.setMap(i, replace); 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 65f81218..9c136017 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java @@ -1,8 +1,5 @@ package com.infernalsuite.asp; -import ca.spottedleaf.dataconverter.converters.DataConverter; -import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; -import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; import com.infernalsuite.asp.api.SlimeDataConverter; import com.infernalsuite.asp.api.SlimeNMSBridge; import com.infernalsuite.asp.api.world.SlimeWorld; @@ -167,7 +164,7 @@ public SlimeWorldInstance getInstance(World world) { @Override public int getCurrentVersion() { - return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + return SharedConstants.getCurrentVersion().dataVersion().version(); } public void registerWorld(SlimeLevelInstance server) { diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/NMSSlimeChunk.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/NMSSlimeChunk.java index ccbc1ea4..99fbb336 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/NMSSlimeChunk.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/NMSSlimeChunk.java @@ -1,5 +1,6 @@ package com.infernalsuite.asp.level; +import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; import com.infernalsuite.asp.Converter; import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; @@ -11,15 +12,19 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import net.kyori.adventure.nbt.LongArrayBinaryTag; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; @@ -27,12 +32,13 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.chunk.PalettedContainerRO; +import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.ticks.SavedTick; +import org.jetbrains.annotations.Nullable; +import net.minecraft.world.level.storage.TagValueOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -129,6 +135,31 @@ public SlimeChunkSection[] getSections() { return sections; } + + + @Override + public @Nullable ListBinaryTag getFluidTicks() { + return SlimeChunkConverter.convertSavedFluidTicks(this.chunk.getTicksForSerialization(chunk.level.getGameTime()).fluids()); + } + + @Override + public @Nullable CompoundBinaryTag getPoiChunkSections() { + NewChunkHolder chunkHolder = NmsUtil.getChunkHolder(chunk); + if(chunkHolder == null) return null; + + PoiChunk slices = chunkHolder.getPoiChunk(); + return getPoiChunkSections(slices); + } + + public CompoundBinaryTag getPoiChunkSections(PoiChunk poiChunk) { + return SlimeChunkConverter.toSlimeSections(poiChunk); + } + + @Override + public @Nullable ListBinaryTag getBlockTicks() { + return SlimeChunkConverter.convertSavedBlockTicks(this.chunk.getTicksForSerialization(chunk.level.getGameTime()).blocks()); + } + @Override public CompoundBinaryTag getHeightMaps() { CompoundBinaryTag.Builder heightMapsTagBuilder = CompoundBinaryTag.builder(); @@ -168,13 +199,17 @@ public List getEntities(ChunkEntitySlices slices) { if (slices == null) return new ArrayList<>(); List entities = new ArrayList<>(slices.entities.size()); - // Work by - for (Entity entity : slices.entities) { - CompoundTag entityNbt = new CompoundTag(); - try { - if (entity.save(entityNbt)) entities.add(Converter.convertTag(entityNbt)); - } catch (final Exception e) { - LOGGER.error("Could not save the entity = {}, exception = {}", entity, e); + try(final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(chunk.getPos()), LOGGER)) { + // Work by + for (Entity entity : slices.entities) { + try { + TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, entity.registryAccess()); + + if (entity.save(tagValueOutput)) + entities.add(Converter.convertTag(tagValueOutput.buildResult())); + } catch (final Exception e) { + LOGGER.error("Could not save the entity = {}, exception = {}", entity, e); + } } } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java index f58a673d..6abd3952 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java @@ -3,6 +3,7 @@ import com.mojang.datafixers.DataFixer; import net.minecraft.core.HolderLookup; import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; import net.minecraft.world.level.storage.DimensionDataStorage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,14 +17,13 @@ */ public class ReadOnlyDimensionDataStorage extends DimensionDataStorage { - public ReadOnlyDimensionDataStorage(Path dataFolder, DataFixer fixerUpper, HolderLookup.Provider registries) { - super(dataFolder, fixerUpper, registries); + public ReadOnlyDimensionDataStorage(SavedData.Context ctx, Path dataFolder, DataFixer fixerUpper, HolderLookup.Provider registries) { + super(ctx, dataFolder, fixerUpper, registries); } - @SuppressWarnings("unchecked") @Override - public @Nullable T get(SavedData.@NotNull Factory factory, @NotNull String name) { - Optional optional = this.cache.get(name); + public @Nullable T get(SavedDataType type) { + Optional optional = this.cache.get(type); if(optional == null) { return null; } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java index f2b41f80..39f41876 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java @@ -4,6 +4,8 @@ import com.infernalsuite.asp.api.world.SlimeChunkSection; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; @@ -82,13 +84,37 @@ public CompoundBinaryTag getUpgradeData() { return this.wrapper.getUpgradeData(); } + @Override + public @Nullable ListBinaryTag getBlockTicks() { + if(shouldDefaultBackToSlimeChunk()) { + return this.safety.getBlockTicks(); + } + return this.wrapper.getBlockTicks(); + } + + @Override + public @Nullable ListBinaryTag getFluidTicks() { + if(shouldDefaultBackToSlimeChunk()) { + return this.safety.getFluidTicks(); + } + return this.wrapper.getFluidTicks(); + } + + @Override + public @Nullable CompoundBinaryTag getPoiChunkSections() { + if(shouldDefaultBackToSlimeChunk()) { + return this.safety.getPoiChunkSections(); + } + return this.wrapper.getPoiChunkSections(); + } + /* -Slime chunks can still be requested but not actually loaded, this caused -some things to not properly save because they are not "loaded" into the chunk. -See ChunkMap#protoChunkToFullChunk -anything in the if statement will not be loaded and is stuck inside the runnable. -Inorder to possibly not corrupt the state, simply refer back to the slime saved object. -*/ + Slime chunks can still be requested but not actually loaded, this caused + some things to not properly save because they are not "loaded" into the chunk. + See ChunkMap#protoChunkToFullChunk + anything in the if statement will not be loaded and is stuck inside the runnable. + Inorder to possibly not corrupt the state, simply refer back to the slime saved object. + */ public boolean shouldDefaultBackToSlimeChunk() { return this.safety != null && !this.wrapper.getChunk().loaded; } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkConverter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkConverter.java index 39e677a3..f48c71ef 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkConverter.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkConverter.java @@ -1,5 +1,6 @@ package com.infernalsuite.asp.level; +import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray; import ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine; import com.infernalsuite.asp.Converter; @@ -8,35 +9,44 @@ import com.infernalsuite.asp.api.world.SlimeChunkSection; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; -import net.minecraft.core.BlockPos; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.minecraft.SharedConstants; import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.UpgradeData; -import net.minecraft.world.level.chunk.status.ChunkStatusTasks; import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.ticks.LevelChunkTicks; +import net.minecraft.world.ticks.SavedTick; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; public class SlimeChunkConverter { + private static final Codec>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf(); + private static final Codec>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf(); + static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeChunk chunk) { int x = chunk.getX(); int z = chunk.getZ(); @@ -100,8 +110,27 @@ static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeC } } - LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); - LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); + LevelChunkTicks blockLevelChunkTicks; + if(chunk.getBlockTicks() != null) { + ListTag tag = (ListTag) Converter.convertTag(chunk.getBlockTicks()); + List> blockList = SavedTick.filterTickListForChunk(BLOCK_TICKS_CODEC.parse(NbtOps.INSTANCE, tag).resultOrPartial().orElse(List.of()), pos); + + blockLevelChunkTicks = new LevelChunkTicks<>(blockList); + } else { + blockLevelChunkTicks = new LevelChunkTicks<>(); + } + + LevelChunkTicks fluidLevelChunkTicks; + if(chunk.getFluidTicks() != null) { + ListTag tag = (ListTag) Converter.convertTag(chunk.getFluidTicks()); + List> fluidList = SavedTick.filterTickListForChunk(FLUID_TICKS_CODEC.parse(NbtOps.INSTANCE, tag).resultOrPartial().orElse(List.of()), pos); + + fluidLevelChunkTicks = new LevelChunkTicks<>(fluidList); + } else { + fluidLevelChunkTicks = new LevelChunkTicks<>(); + } + + UpgradeData upgradeData; if (chunk.getUpgradeData() != null) { upgradeData = new UpgradeData((net.minecraft.nbt.CompoundTag) Converter.convertTag(chunk.getUpgradeData()), instance); @@ -111,7 +140,7 @@ static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeC LevelChunk.PostLoadProcessor processor = SerializableChunkData.postLoadChunk( instance, - chunk.getEntities().stream().map(tag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)).toList(), + new ArrayList<>(), //Entities are loaded by moonrise chunk.getTileEntities().stream().map(tag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)).toList() ); @@ -151,4 +180,37 @@ static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeC return nmsChunk; } + + public static ListBinaryTag convertSavedFluidTicks(List> ticks) { + Tag tag = FLUID_TICKS_CODEC.encodeStart(NbtOps.INSTANCE, ticks).getOrThrow(); + return Converter.convertTag(tag); + } + + public static ListBinaryTag convertSavedBlockTicks(List> ticks) { + Tag tag = BLOCK_TICKS_CODEC.encodeStart(NbtOps.INSTANCE, ticks).getOrThrow(); + return Converter.convertTag(tag); + } + + public static CompoundTag createPoiChunk(SlimeChunk chunk) { + return createPoiChunkFromSlimeSections(chunk.getPoiChunkSections(), SharedConstants.getCurrentVersion().dataVersion().version()); + } + + public static CompoundTag createPoiChunkFromSlimeSections(CompoundBinaryTag slimePoiSections, int dataVersion) { + CompoundTag tag = new CompoundTag(); + tag.put("Sections", Converter.convertTag(slimePoiSections)); + tag.putInt("DataVersion", dataVersion); + return tag; + } + + public static CompoundBinaryTag toSlimeSections(PoiChunk poiChunk) { + CompoundTag save = poiChunk.save(); + return getSlimeSectionsFromPoiCompound(save); + } + + public static CompoundBinaryTag getSlimeSectionsFromPoiCompound(CompoundTag save) { + if(save == null) return null; + + CompoundTag sections = save.getCompoundOrEmpty("Sections"); + return Converter.convertTag(sections); + } } diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeInMemoryWorld.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeInMemoryWorld.java index f42eb9ff..deb15e5d 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeInMemoryWorld.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeInMemoryWorld.java @@ -1,10 +1,12 @@ package com.infernalsuite.asp.level; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; +import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; import com.infernalsuite.asp.Converter; import com.infernalsuite.asp.Util; import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.pdc.AdventurePersistentDataContainer; import com.infernalsuite.asp.serialization.slime.SlimeSerializer; import com.infernalsuite.asp.skeleton.SkeletonCloning; @@ -18,8 +20,10 @@ import net.kyori.adventure.nbt.CompoundBinaryTag; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.nbt.ListBinaryTag; import net.minecraft.SharedConstants; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.material.Fluid; @@ -101,7 +105,7 @@ public LevelChunk createChunk(int x, int z, SlimeChunk chunk) { // Authored by: Kenox // Don't use the NMS live chunk in the chunk map - public void unload(LevelChunk providedChunk, ChunkEntitySlices slices) { + public void unload(LevelChunk providedChunk, ChunkEntitySlices slices, PoiChunk poiChunk) { final int x = providedChunk.locX; final int z = providedChunk.locZ; @@ -117,6 +121,25 @@ public void unload(LevelChunk providedChunk, ChunkEntitySlices slices) { } chunk.updatePersistentDataContainer(); + ListBinaryTag blockTicks = null; + ListBinaryTag fluidTicks = null; + //Only save this data into memory when we actually want it there + if(getPropertyMap().getValue(SlimeProperties.SAVE_BLOCK_TICKS) || getPropertyMap().getValue(SlimeProperties.SAVE_FLUID_TICKS)) { + ChunkAccess.PackedTicks ticksForSerialization = chunk.getChunk().getTicksForSerialization(this.instance.getGameTime()); + + if(getPropertyMap().getValue(SlimeProperties.SAVE_BLOCK_TICKS)) { + blockTicks = SlimeChunkConverter.convertSavedBlockTicks(ticksForSerialization.blocks()); + } + if(getPropertyMap().getValue(SlimeProperties.SAVE_FLUID_TICKS)) { + fluidTicks = SlimeChunkConverter.convertSavedFluidTicks(ticksForSerialization.fluids()); + } + } + CompoundBinaryTag poiSections = null; + if(getPropertyMap().getValue(SlimeProperties.SAVE_POI)) { + poiSections = chunk.getPoiChunkSections(poiChunk); + } + + this.chunkStorage.put(Util.chunkPosition(x, z), new SlimeChunkSkeleton( chunk.getX(), chunk.getZ(), @@ -125,7 +148,10 @@ public void unload(LevelChunk providedChunk, ChunkEntitySlices slices) { chunk.getTileEntities(), chunk.getEntities(slices), //As the slices are not available anymore, we can't get the entities otherwise chunk.getExtraData(), - null + null, + poiSections, + blockTicks, + fluidTicks )); } @@ -164,6 +190,8 @@ public SlimeWorld getSerializableCopy() { chunk = nmsSlimeChunk.getChunk(); } + + if (chunk != null) { if (FastChunkPruner.canBePruned(world, chunk)) { continue; @@ -174,6 +202,21 @@ public SlimeWorld getSerializableCopy() { CompoundBinaryTag adventureTag = Converter.convertTag(chunk.persistentDataContainer.toTagCompound()); clonedChunk.getExtraData().put("ChunkBukkitValues", adventureTag); + ListBinaryTag blockTicks = null; + ListBinaryTag fluidTicks = null; + //Only save this data into memory when we actually want it there + if(getPropertyMap().getValue(SlimeProperties.SAVE_BLOCK_TICKS) || getPropertyMap().getValue(SlimeProperties.SAVE_FLUID_TICKS)) { + ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(this.instance.getGameTime()); + + if(getPropertyMap().getValue(SlimeProperties.SAVE_BLOCK_TICKS)) { + blockTicks = SlimeChunkConverter.convertSavedBlockTicks(ticksForSerialization.blocks()); + } + if(getPropertyMap().getValue(SlimeProperties.SAVE_FLUID_TICKS)) { + fluidTicks = SlimeChunkConverter.convertSavedFluidTicks(ticksForSerialization.fluids()); + } + } + + clonedChunk = new SlimeChunkSkeleton( clonedChunk.getX(), clonedChunk.getZ(), @@ -182,7 +225,10 @@ public SlimeWorld getSerializableCopy() { clonedChunk.getTileEntities(), clonedChunk.getEntities(), clonedChunk.getExtraData(), - clonedChunk.getUpgradeData() + clonedChunk.getUpgradeData(), + clonedChunk.getPoiChunkSections(), + blockTicks, + fluidTicks ); } } @@ -196,7 +242,7 @@ public SlimeWorld getSerializableCopy() { this.instance.getWorld().storeBukkitValues(nmsTag); // Bukkit stores the relevant tag as a tag with the key "BukkitValues" in the tag we supply to it - var adventureTag = Converter.convertTag(nmsTag.getCompound("BukkitValues")); + var adventureTag = Converter.convertTag(nmsTag.getCompoundOrEmpty("BukkitValues")); world.getExtraData().put("BukkitValues", adventureTag); return new SkeletonSlimeWorld(world.getName(), @@ -254,7 +300,7 @@ public SlimeWorld clone(String worldName, SlimeLoader loader) throws WorldAlread @Override public int getDataVersion() { - return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + return SharedConstants.getCurrentVersion().dataVersion().version(); } @Override diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java index fcf71851..8535ddf4 100644 --- a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java @@ -1,5 +1,6 @@ package com.infernalsuite.asp.level; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.mojang.serialization.MapCodec; import net.minecraft.core.Holder; import net.minecraft.world.level.biome.Biome; @@ -14,8 +15,11 @@ public class SlimeLevelGenerator extends FlatLevelSource { - public SlimeLevelGenerator(Holder biome) { + private final SlimeLevelInstance level; + + public SlimeLevelGenerator(Holder biome, SlimeLevelInstance level) { super(new FlatLevelGeneratorSettings(Optional.empty(), biome, List.of()), getSource(biome)); + this.level = level; } private static BiomeSource getSource(Holder biome) { @@ -36,4 +40,9 @@ public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler noise) { } }; } + + @Override + public int getSeaLevel() { + return level.getSlimeInstance().getPropertyMap().getValue(SlimeProperties.SEA_LEVEL); + } } 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 c5fbe3a6..f56c7e44 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 @@ -2,10 +2,13 @@ import ca.spottedleaf.concurrentutil.util.Priority; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; +import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkLoadTask; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.infernalsuite.asp.level.moonrise.SlimeEntityDataLoader; +import com.infernalsuite.asp.level.moonrise.SlimePoiDataLoader; import com.infernalsuite.asp.serialization.slime.SlimeSerializer; import com.infernalsuite.asp.api.world.SlimeWorld; import com.infernalsuite.asp.api.world.SlimeWorldInstance; @@ -25,6 +28,7 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkGenerator; 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.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; @@ -95,9 +99,20 @@ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primar propertyMap.getValue(SlimeProperties.SPAWN_Y), propertyMap.getValue(SlimeProperties.SPAWN_Z)), propertyMap.getValue(SlimeProperties.SPAWN_YAW)); - super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS)); + super.chunkSource.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); + + this.entityDataController = new SlimeEntityDataLoader( + new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController.EntityRegionFileStorage( + new RegionStorageInfo(levelStorageAccess.getLevelId(), worldKey, "entities"), + levelStorageAccess.getDimensionPath(worldKey).resolve("entities"), + MinecraftServer.getServer().forceSynchronousWrites() + ), + this.chunkTaskScheduler, + this + ); + this.poiDataController = new SlimePoiDataLoader(this, this.chunkTaskScheduler); } @Override @@ -105,7 +120,7 @@ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primar String biomeStr = slimeBootstrap.initial().getPropertyMap().getValue(SlimeProperties.DEFAULT_BIOME); ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, ResourceLocation.parse(biomeStr)); Holder defaultBiome = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME).get(biomeKey).orElseThrow(); - return new SlimeLevelGenerator(defaultBiome); + return new SlimeLevelGenerator(defaultBiome, this); } @Override @@ -113,8 +128,8 @@ public void save(@Nullable ProgressListener progressUpdate, boolean forceSave, b if (!savingDisabled) save(); } - public void unload(@NotNull LevelChunk chunk, ChunkEntitySlices slices) { - slimeInstance.unload(chunk, slices); + public void unload(@NotNull LevelChunk chunk, ChunkEntitySlices slices, PoiChunk poiChunk) { + slimeInstance.unload(chunk, slices, poiChunk); } @Override diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimeEntityDataLoader.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimeEntityDataLoader.java new file mode 100644 index 00000000..c452c154 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimeEntityDataLoader.java @@ -0,0 +1,54 @@ +package com.infernalsuite.asp.level.moonrise; + +import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController; +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; +import com.infernalsuite.asp.Converter; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.level.SlimeLevelInstance; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +public class SlimeEntityDataLoader extends EntityDataController { + + private final SlimeLevelInstance instance; + + public SlimeEntityDataLoader(EntityRegionFileStorage storage, ChunkTaskScheduler taskScheduler, SlimeLevelInstance instance) { + super(storage, taskScheduler); + this.instance = instance; + } + + @Override + public WriteData startWrite(int chunkX, int chunkZ, CompoundTag compound) { + /* + * We don't support saving with moonrise as that is way too slow and prevents us from having fast world unloads + */ + throw new UnsupportedOperationException("Slime should use custom saving method? Is PaperHooks#forceNoSave broken?"); + } + + @Override + public ReadData readData(int chunkX, int chunkZ) { + SlimeChunk chunk = instance.getSlimeInstance().getChunk(chunkX, chunkZ); + + if(chunk == null || chunk.getEntities() == null) { + return new ReadData(ReadData.ReadResult.NO_DATA, null, null, 0); + } + + CompoundTag tag = new CompoundTag(); + tag.putIntArray("Position", new int[]{chunkX, chunkZ}); + tag.putInt("DataVersion", instance.getSlimeInstance().getDataVersion()); + + ListTag listTag = new ListTag(); + for (CompoundBinaryTag entity : chunk.getEntities()) { + listTag.add(Converter.convertTag(entity)); + } + tag.put("Entities", listTag); + + return new ReadData(ReadData.ReadResult.SYNC_READ, null, tag, 0); + } + + @Override + public CompoundTag finishRead(int chunkX, int chunkZ, ReadData readData) { + return readData.syncRead(); + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimePoiDataLoader.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimePoiDataLoader.java new file mode 100644 index 00000000..8732c4cd --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/moonrise/SlimePoiDataLoader.java @@ -0,0 +1,46 @@ +package com.infernalsuite.asp.level.moonrise; + +import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController; +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; +import com.infernalsuite.asp.Converter; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.level.SlimeChunkConverter; +import com.infernalsuite.asp.level.SlimeLevelInstance; +import net.minecraft.SharedConstants; +import net.minecraft.nbt.CompoundTag; + +public class SlimePoiDataLoader extends PoiDataController { + + private final SlimeLevelInstance instance; + + public SlimePoiDataLoader(SlimeLevelInstance instance, ChunkTaskScheduler taskScheduler) { + super(instance, taskScheduler); + this.instance = instance; + } + + @Override + public WriteData startWrite(int chunkX, int chunkZ, CompoundTag compound) { + /* + * We don't support saving with moonrise as that is way too slow and prevents us from having fast world unloads + */ + throw new UnsupportedOperationException("Slime should use custom saving method? Is PaperHooks#forceNoSave broken?"); + } + + @Override + public ReadData readData(int chunkX, int chunkZ) { + SlimeChunk chunk = instance.getSlimeInstance().getChunk(chunkX, chunkZ); + + if(chunk == null || chunk.getPoiChunkSections() == null) { + return new ReadData(ReadData.ReadResult.NO_DATA, null, null, 0); + } + + + CompoundTag tag = SlimeChunkConverter.createPoiChunk(chunk); + return new ReadData(ReadData.ReadResult.SYNC_READ, null, tag, 0); + } + + @Override + public CompoundTag finishRead(int chunkX, int chunkZ, ReadData readData) { + return readData.syncRead(); + } +} diff --git a/build-data/aspaper.at b/build-data/aspaper.at index 2014669a..7a9085f5 100644 --- a/build-data/aspaper.at +++ b/build-data/aspaper.at @@ -1,7 +1,12 @@ # This file is auto generated, any changes may be overridden! # See CONTRIBUTING.md on how to add access transformers. +protected-f net.minecraft.server.level.ServerLevel chunkTaskScheduler +protected-f net.minecraft.server.level.ServerLevel entityDataController +protected-f net.minecraft.server.level.ServerLevel poiDataController public ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices entities public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkLoadTask chunkHolder public net.minecraft.server.MinecraftServer commandStorage public net.minecraft.world.level.chunk.storage.SerializableChunkData makeBiomeCodec(Lnet/minecraft/core/Registry;)Lcom/mojang/serialization/Codec; public net.minecraft.world.level.chunk.storage.SerializableChunkData postLoadChunk(Lnet/minecraft/server/level/ServerLevel;Ljava/util/List;Ljava/util/List;)Lnet/minecraft/world/level/chunk/LevelChunk$PostLoadProcessor; +public-f ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController +public-f ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController diff --git a/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java b/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java index f71da1ed..28a1bb3f 100644 --- a/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java +++ b/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java @@ -14,4 +14,8 @@ public static void debug(String message) { LOGGER.log(Level.WARNING, message); } } + + public static void warn(String message) { + LOGGER.log(Level.WARNING, message); + } } \ No newline at end of file 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 7a982024..952be90d 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 @@ -108,20 +108,6 @@ public SlimeWorld readFromData(AnvilImportData importData) { throw new InvalidWorldException(environmentDir); } - // World maps -// File dataDir = new File(worldDir, "data"); -// List maps = new ArrayList<>(); -// -// if (dataDir.exists()) { -// if (!dataDir.isDirectory()) { -// throw new InvalidWorldException(worldDir); -// } -// -// for (File mapFile : dataDir.listFiles((dir, name) -> MAP_FILE_PATTERN.matcher(name).matches())) { -// maps.add(loadMap(mapFile)); -// } -// } - propertyMap.setValue(SlimeProperties.SPAWN_X, data.x); propertyMap.setValue(SlimeProperties.SPAWN_Y, data.y); propertyMap.setValue(SlimeProperties.SPAWN_Z, data.z); @@ -134,22 +120,6 @@ public SlimeWorld readFromData(AnvilImportData importData) { } } - private static CompoundBinaryTag loadMap(File mapFile) throws IOException { - String fileName = mapFile.getName(); - int mapId = Integer.parseInt(fileName.substring(4, fileName.length() - 4)); - CompoundBinaryTag tag = BinaryTagIO.unlimitedReader().read(new BufferedInputStream(new FileInputStream(mapFile))).getCompound("data"); - tag.put("id", IntBinaryTag.intBinaryTag(mapId)); - return tag; - } - - private static CompoundBinaryTag loadMap(Path mapFile) throws IOException { - String fileName = mapFile.getFileName().toString(); - int mapId = Integer.parseInt(fileName.substring(4, fileName.length() - 4)); - CompoundBinaryTag tag = BinaryTagIO.unlimitedReader().read(mapFile).getCompound("data"); - tag.put("id", IntBinaryTag.intBinaryTag(mapId)); - return tag; - } - private static LevelData readLevelData(Path file) throws IOException, InvalidWorldException { CompoundBinaryTag tag; @@ -268,7 +238,10 @@ private static void readEntityChunk(CompoundBinaryTag compound, int worldVersion chunk.getTileEntities(), entities, chunk.getExtraData(), - chunk.getUpgradeData() + chunk.getUpgradeData(), + chunk.getPoiChunkSections(), + chunk.getBlockTicks(), + chunk.getFluidTicks() )); } } @@ -329,7 +302,7 @@ private static SlimeChunk readChunk(CompoundBinaryTag compound, int worldVersion return Arrays.stream(sectionArray) .filter(Objects::nonNull) .findFirst() - .map(x -> new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMaps, tileEntities, entities, extraTag, null)) + .map(x -> new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMaps, tileEntities, entities, extraTag, null, null, null, null)) //TODO: Convert poi, block and fluid .orElse(null); } 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 7252a640..3a807650 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 @@ -5,7 +5,9 @@ import com.infernalsuite.asp.api.world.SlimeChunk; import com.infernalsuite.asp.api.world.SlimeChunkSection; import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.serialization.slime.reader.impl.v13.v13AdditionalWorldData; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagTypes; @@ -17,11 +19,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; public class SlimeSerializer { @@ -49,8 +47,20 @@ public static byte[] serialize(SlimeWorld world) { // World version outStream.writeInt(world.getDataVersion()); + EnumSet additionalWorldData = EnumSet.noneOf(v13AdditionalWorldData.class); + if (world.getPropertyMap().getValue(SlimeProperties.SAVE_POI)) { + additionalWorldData.add(v13AdditionalWorldData.POI_CHUNKS); + } + if (world.getPropertyMap().getValue(SlimeProperties.SAVE_BLOCK_TICKS)) { + additionalWorldData.add(v13AdditionalWorldData.BLOCK_TICKS); + } + if (world.getPropertyMap().getValue(SlimeProperties.SAVE_FLUID_TICKS)) { + additionalWorldData.add(v13AdditionalWorldData.FLUID_TICKS); + } + outStream.writeByte(v13AdditionalWorldData.fromSet(additionalWorldData)); + // Chunks - byte[] chunkData = serializeChunks(world, world.getChunkStorage()); + byte[] chunkData = serializeChunks(world, world.getChunkStorage(), additionalWorldData); byte[] compressedChunkData = Zstd.compress(chunkData); outStream.writeInt(compressedChunkData.length); @@ -72,7 +82,7 @@ public static byte[] serialize(SlimeWorld world) { return outByteStream.toByteArray(); } - static byte[] serializeChunks(SlimeWorld world, Collection chunks) throws IOException { + static byte[] serializeChunks(SlimeWorld world, Collection chunks, EnumSet data) throws IOException { ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); DataOutputStream outStream = new DataOutputStream(outByteStream); @@ -91,18 +101,25 @@ static byte[] serializeChunks(SlimeWorld world, Collection chunks) t outStream.writeInt(sections.length); for (SlimeChunkSection slimeChunkSection : sections) { - // Block Light + byte sectionFlags = 0; + boolean hasBlockLight = slimeChunkSection.getBlockLight() != null; - outStream.writeBoolean(hasBlockLight); + boolean hasSkyLight = slimeChunkSection.getSkyLight() != null; + if(hasBlockLight) { + sectionFlags = (byte) (sectionFlags | 1); + } + if(hasSkyLight) { + sectionFlags = (byte) (sectionFlags | (1 << 1)); + } + outStream.write(sectionFlags); + + // Block Light if (hasBlockLight) { outStream.write(slimeChunkSection.getBlockLight().getBacking()); } // Sky Light - boolean hasSkyLight = slimeChunkSection.getSkyLight() != null; - outStream.writeBoolean(hasSkyLight); - if (hasSkyLight) { outStream.write(slimeChunkSection.getSkyLight().getBacking()); } @@ -122,6 +139,24 @@ static byte[] serializeChunks(SlimeWorld world, Collection chunks) t outStream.writeInt(heightMaps.length); outStream.write(heightMaps); + if (data.contains(v13AdditionalWorldData.POI_CHUNKS)) { + byte[] poiData = serializeCompoundTag(chunk.getPoiChunkSections()); + outStream.writeInt(poiData.length); + outStream.write(poiData); + } + + if (data.contains(v13AdditionalWorldData.BLOCK_TICKS)) { + byte[] blockTicksData = serializeCompoundTag(wrap("block_ticks", chunk.getBlockTicks())); + outStream.writeInt(blockTicksData.length); + outStream.write(blockTicksData); + } + + if (data.contains(v13AdditionalWorldData.FLUID_TICKS)) { + byte[] fluidTicksData = serializeCompoundTag(wrap("fluid_ticks", chunk.getFluidTicks())); + outStream.writeInt(fluidTicksData.length); + outStream.write(fluidTicksData); + } + // Tile entities ListBinaryTag tileEntitiesNbtList = ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, yayGenerics(chunk.getTileEntities())); CompoundBinaryTag tileEntitiesCompound = CompoundBinaryTag.builder().put("tileEntities", tileEntitiesNbtList).build(); @@ -151,8 +186,15 @@ static byte[] serializeChunks(SlimeWorld world, Collection chunks) t return outByteStream.toByteArray(); } + private static CompoundBinaryTag wrap(String key, ListBinaryTag list) { + if(list == null || list.isEmpty()) { + return null; + } + return CompoundBinaryTag.builder().put(key, list).build(); + } + protected static byte[] serializeCompoundTag(CompoundBinaryTag tag) throws IOException { - if (tag == null || tag.size() == 0) return new byte[0]; + if (tag == null || tag.isEmpty()) return new byte[0]; ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); BinaryTagIO.writer().write(tag, outByteStream); diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java index 57ff7f25..4e7f5879 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java @@ -9,6 +9,7 @@ import com.infernalsuite.asp.serialization.slime.reader.impl.v10.v10WorldFormat; import com.infernalsuite.asp.serialization.slime.reader.impl.v11.v11WorldFormat; import com.infernalsuite.asp.serialization.slime.reader.impl.v12.v12WorldFormat; +import com.infernalsuite.asp.serialization.slime.reader.impl.v13.v13WorldFormat; import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9WorldFormat; import java.io.ByteArrayInputStream; @@ -27,6 +28,7 @@ public class SlimeWorldReaderRegistry { register(v10WorldFormat.FORMAT, 10); register(v11WorldFormat.FORMAT, 11); register(v12WorldFormat.FORMAT, 12); + register(v13WorldFormat.FORMAT, 13); } private static void register(VersionedByteSlimeWorldReader format, int... bytes) { 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 e83dbb54..a33fd864 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 @@ -13,9 +13,11 @@ import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; +import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.kyori.adventure.nbt.*; +import org.jetbrains.annotations.NotNull; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -49,7 +51,7 @@ public SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worl // Entity deserialization CompoundBinaryTag entitiesCompound = readCompound(entities); - if(entitiesCompound != null) { + if(!entitiesCompound.isEmpty()) { for (BinaryTag binaryTag : entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND)) { CompoundBinaryTag entityCompound = (CompoundBinaryTag) binaryTag; @@ -67,7 +69,7 @@ public SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worl // Tile Entity deserialization CompoundBinaryTag tileEntitiesCompound = readCompound(tileEntities); - if(tileEntitiesCompound != null) { + if(!tileEntitiesCompound.isEmpty()) { for (BinaryTag binaryTag : (tileEntitiesCompound.getList("tiles", BinaryTagTypes.COMPOUND))) { CompoundBinaryTag tileEntityCompound = (CompoundBinaryTag) binaryTag; @@ -102,7 +104,7 @@ public SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worl } ConcurrentMap extraData = new ConcurrentHashMap<>(); - if (extraCompound != null) extraCompound.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + extraCompound.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraData, @@ -173,7 +175,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope } chunkMap.put(Util.chunkPosition(x, z), - new com.infernalsuite.asp.skeleton.SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>(), new HashMap<>(), null) + new SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>(), new HashMap<>(), null, null, null, null) ); } } @@ -201,8 +203,8 @@ private static byte[] readCompressed(DataInputStream stream) throws IOException return normal; } - private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { - if (tagBytes.length == 0) return null; + private static @NotNull CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { + if (tagBytes.length == 0) return CompoundBinaryTag.empty(); return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); } 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 b4a99a1c..d5480903 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 @@ -11,6 +11,7 @@ import com.infernalsuite.asp.api.world.SlimeWorld; import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.kyori.adventure.nbt.*; @@ -41,7 +42,7 @@ public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, S CompoundBinaryTag extraTag = readCompound(extraTagBytes); SlimePropertyMap worldPropertyMap = propertyMap; - CompoundBinaryTag propertiesMap = extraTag != null && extraTag.get("properties") != null + CompoundBinaryTag propertiesMap = extraTag.get("properties") != null ? extraTag.getCompound("properties") : null; @@ -54,7 +55,7 @@ public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, S } ConcurrentMap extraData = new ConcurrentHashMap<>(); - if (extraTag != null) extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraData, worldPropertyMap, worldVersion); } @@ -123,7 +124,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope chunkData.read(compressedTileEntitiesData); Zstd.decompress(decompressedTileEntitiesData, compressedTileEntitiesData); - CompoundBinaryTag tileEntitiesCompound = readCompoundOrEmpty(decompressedTileEntitiesData); + CompoundBinaryTag tileEntitiesCompound = readCompound(decompressedTileEntitiesData); ListBinaryTag tileEntitiesTag = tileEntitiesCompound.getList("tileEntities", BinaryTagTypes.COMPOUND); List serializedTileEntities = new ArrayList<>(tileEntitiesTag.size()); @@ -140,7 +141,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope chunkData.read(compressedEntitiesData); Zstd.decompress(decompressedEntitiesData, compressedEntitiesData); - CompoundBinaryTag entitiesCompound = readCompoundOrEmpty(decompressedEntitiesData); + CompoundBinaryTag entitiesCompound = readCompound(decompressedEntitiesData); ListBinaryTag entitiesTag = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND); List serializedEntities = new ArrayList<>(entitiesTag.size()); for (BinaryTag binaryTag : entitiesTag) { @@ -148,7 +149,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope } chunkMap.put(Util.chunkPosition(x, z), - new com.infernalsuite.asp.skeleton.SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, new HashMap<>(), null)); + new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, new HashMap<>(), null, null, null, null)); } return chunkMap; } @@ -164,14 +165,8 @@ private static byte[] readCompressed(DataInputStream stream) throws IOException } private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { - if (tagBytes.length == 0) return null; + if (tagBytes.length == 0) return CompoundBinaryTag.empty(); return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); } - - private static CompoundBinaryTag readCompoundOrEmpty(byte[] tagBytes) throws IOException { - CompoundBinaryTag tag = readCompound(tagBytes); - if(tag == null) return CompoundBinaryTag.empty(); - return tag; - } } 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 87faf2c3..2593f5f4 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 @@ -11,6 +11,7 @@ import com.infernalsuite.asp.api.world.SlimeWorld; import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -18,6 +19,7 @@ import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagTypes; import net.kyori.adventure.nbt.CompoundBinaryTag; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; @@ -45,7 +47,7 @@ public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, S CompoundBinaryTag extraTag = readCompound(extraTagBytes); ConcurrentMap extraData = new ConcurrentHashMap<>(); - if (extraTag != null) extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); SlimePropertyMap worldPropertyMap = propertyMap; if (extraData.containsKey("properties")) { @@ -104,7 +106,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope chunkData.read(biomeData); CompoundBinaryTag biomeTag = readCompound(biomeData); - chunkSections[sectionId] = new com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); + chunkSections[sectionId] = new SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); } // HeightMaps @@ -117,7 +119,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope byte[] tileEntitiesRaw = read(chunkData); List tileEntities; CompoundBinaryTag tileEntitiesCompound = readCompound(tileEntitiesRaw); - if (tileEntitiesCompound == null) { + if (tileEntitiesCompound.isEmpty()) { tileEntities = Collections.emptyList(); } else { tileEntities = tileEntitiesCompound.getList("tileEntities", BinaryTagTypes.COMPOUND).stream() @@ -130,7 +132,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope byte[] entitiesRaw = read(chunkData); List entities; CompoundBinaryTag entitiesCompound = readCompound(entitiesRaw); - if (entitiesCompound == null) { + if (entitiesCompound.isEmpty()) { entities = Collections.emptyList(); } else { entities = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND).stream() @@ -143,9 +145,9 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope CompoundBinaryTag extra = readCompound(rawExtra); Map extraData = new HashMap<>(); - if (extra != null) extra.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + extra.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); - chunkMap.put(Util.chunkPosition(x, z), new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, tileEntities, entities, extraData, null)); + chunkMap.put(Util.chunkPosition(x, z), new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, tileEntities, entities, extraData, null, null, null, null)); } return chunkMap; } @@ -167,8 +169,8 @@ private static byte[] read(DataInputStream stream) throws IOException { return data; } - private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { - if (tagBytes.length == 0) return null; + private static @NotNull CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { + if (tagBytes.length == 0) return CompoundBinaryTag.empty(); return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); } diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13AdditionalWorldData.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13AdditionalWorldData.java new file mode 100644 index 00000000..3d58289e --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13AdditionalWorldData.java @@ -0,0 +1,32 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v13; + +import java.util.EnumSet; + +public enum v13AdditionalWorldData { + POI_CHUNKS, + BLOCK_TICKS, + FLUID_TICKS; + + public boolean isSet(byte bitset) { + return ((bitset >> ordinal()) & 1) == 1; + } + + public static int countUnsupportedFlags(byte bitset) { + int supportedFlagsMask = 0; + for (v13AdditionalWorldData data : v13AdditionalWorldData.values()) { + supportedFlagsMask |= (1 << data.ordinal()); + } + int unsupportedFlagsMask = bitset & ~supportedFlagsMask; + return Integer.bitCount(unsupportedFlagsMask); + } + + public static byte fromSet(EnumSet set) { + byte bitset = 0; + for (v13AdditionalWorldData data : set) { + bitset = (byte) (bitset | (1 << data.ordinal())); + } + return bitset; + } + + +} 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 new file mode 100644 index 00000000..d4e4e715 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13SlimeWorldDeSerializer.java @@ -0,0 +1,207 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v13; + +import com.github.luben.zstd.Zstd; +import com.infernalsuite.asp.SlimeLogger; +import com.infernalsuite.asp.Util; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.utils.NibbleArray; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.nbt.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class v13SlimeWorldDeSerializer implements com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader { + + public static final int ARRAY_SIZE = 16 * 16 * 16 / (8 / 4); + + @Override + public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, String worldName, DataInputStream dataStream, SlimePropertyMap propertyMap, boolean readOnly) throws IOException, CorruptedWorldException, NewerFormatException { + int worldVersion = dataStream.readInt(); + byte additionalWorldData = dataStream.readByte(); + + byte[] chunkBytes = readCompressed(dataStream); + Long2ObjectMap chunks = readChunks(propertyMap, additionalWorldData, chunkBytes); + + byte[] extraTagBytes = readCompressed(dataStream); + CompoundBinaryTag extraTag = readCompound(extraTagBytes); + + ConcurrentMap extraData = new ConcurrentHashMap<>(); + extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + + SlimePropertyMap worldPropertyMap = propertyMap; + if (extraData.containsKey("properties")) { + CompoundBinaryTag serializedSlimeProperties = (CompoundBinaryTag) extraData.get("properties"); + worldPropertyMap = SlimePropertyMap.fromCompound(serializedSlimeProperties); + worldPropertyMap.merge(propertyMap); + } + + return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraData, worldPropertyMap, worldVersion); + } + + private static Long2ObjectMap readChunks(SlimePropertyMap slimePropertyMap, byte additionalWorldData, byte[] chunkBytes) throws IOException { + Long2ObjectMap chunkMap = new Long2ObjectOpenHashMap<>(); + DataInputStream chunkData = new DataInputStream(new ByteArrayInputStream(chunkBytes)); + + int chunks = chunkData.readInt(); + for (int i = 0; i < chunks; i++) { + // ChunkPos + int x = chunkData.readInt(); + 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(); + for (int sectionId = 0; sectionId < sectionCount; sectionId++) { + byte sectionFlags = chunkData.readByte(); + + // Block Light Nibble Array + NibbleArray blockLightArray; + if ((sectionFlags & 1) == 1) { + byte[] blockLightByteArray = new byte[ARRAY_SIZE]; + chunkData.read(blockLightByteArray); + blockLightArray = new NibbleArray(blockLightByteArray); + } else { + blockLightArray = null; + } + + // Sky Light Nibble Array + NibbleArray skyLightArray; + if (((sectionFlags >> 1) & 1) == 1) { + byte[] skyLightByteArray = new byte[ARRAY_SIZE]; + chunkData.read(skyLightByteArray); + skyLightArray = new NibbleArray(skyLightByteArray); + } else { + skyLightArray = null; + } + + // Block Data + byte[] blockStateData = new byte[chunkData.readInt()]; + chunkData.read(blockStateData); + CompoundBinaryTag blockStateTag = readCompound(blockStateData); + + // Biome Data + byte[] biomeData = new byte[chunkData.readInt()]; + chunkData.read(biomeData); + CompoundBinaryTag biomeTag = readCompound(biomeData); + + chunkSections[sectionId] = new com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); + } + + // HeightMaps + byte[] heightMapData = new byte[chunkData.readInt()]; + chunkData.read(heightMapData); + CompoundBinaryTag heightMaps = readCompound(heightMapData); + + CompoundBinaryTag poiChunk = null; + if(v13AdditionalWorldData.POI_CHUNKS.isSet(additionalWorldData)) { + byte[] poiData = new byte[chunkData.readInt()]; + chunkData.read(poiData); + poiChunk = readCompound(poiData); + } + + ListBinaryTag blockTicks = null; + if(v13AdditionalWorldData.BLOCK_TICKS.isSet(additionalWorldData)) { + byte[] blockTickData = new byte[chunkData.readInt()]; + chunkData.read(blockTickData); + CompoundBinaryTag tag = readCompound(blockTickData); + blockTicks = tag.getList("block_ticks", BinaryTagTypes.COMPOUND); + } + ListBinaryTag fluidTicks = null; + if(v13AdditionalWorldData.FLUID_TICKS.isSet(additionalWorldData)) { + byte[] fluidTickData = new byte[chunkData.readInt()]; + chunkData.read(fluidTickData); + CompoundBinaryTag tag = readCompound(fluidTickData); + fluidTicks = tag.getList("fluid_ticks", BinaryTagTypes.COMPOUND); + } + + int countOfUnsupportedData = v13AdditionalWorldData.countUnsupportedFlags(additionalWorldData); + for (int i1 = 0; i1 < countOfUnsupportedData; i1++) { + byte[] randomData = new byte[chunkData.readInt()]; + chunkData.read(randomData); + } + if(countOfUnsupportedData > 0) { + SlimeLogger.warn("Unsupported additional world data found in chunk " + x + ", " + z + ". This should not cause any issues, however this data will be lost on save."); + } + + // Tile Entities + + byte[] tileEntitiesRaw = read(chunkData); + List tileEntities; + CompoundBinaryTag tileEntitiesCompound = readCompound(tileEntitiesRaw); + if (tileEntitiesCompound.isEmpty()) { + tileEntities = Collections.emptyList(); + } else { + tileEntities = tileEntitiesCompound.getList("tileEntities", BinaryTagTypes.COMPOUND).stream() + .map(tag -> (CompoundBinaryTag) tag) + .toList(); + } + + // Entities + + byte[] entitiesRaw = read(chunkData); + List entities; + CompoundBinaryTag entitiesCompound = readCompound(entitiesRaw); + if (entitiesCompound.isEmpty()) { + entities = Collections.emptyList(); + } else { + entities = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND).stream() + .map(tag -> (CompoundBinaryTag) tag) + .toList(); + } + + // Extra Tag + byte[] rawExtra = read(chunkData); + CompoundBinaryTag extra = readCompound(rawExtra); + + Map extraData = new HashMap<>(); + extra.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + + chunkMap.put(Util.chunkPosition(x, z), new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, tileEntities, entities, extraData, null, poiChunk, blockTicks, fluidTicks)); + } + return chunkMap; + } + + private static byte[] readCompressed(DataInputStream stream) throws IOException { + int compressedLength = stream.readInt(); + int decompressedLength = stream.readInt(); + byte[] compressedData = new byte[compressedLength]; + byte[] decompressedData = new byte[decompressedLength]; + stream.read(compressedData); + Zstd.decompress(decompressedData, compressedData); + return decompressedData; + } + + private static byte[] read(DataInputStream stream) throws IOException { + int length = stream.readInt(); + byte[] data = new byte[length]; + stream.read(data); + return data; + } + + private static @NotNull CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { + if (tagBytes.length == 0) return CompoundBinaryTag.empty(); + + return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); + } +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13WorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13WorldFormat.java new file mode 100644 index 00000000..0ae5c287 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v13/v13WorldFormat.java @@ -0,0 +1,10 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v13; + +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat; + +public interface v13WorldFormat { + + SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v13SlimeWorldDeSerializer()); + +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java index 64ed2195..5945f41d 100644 --- a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java @@ -67,7 +67,10 @@ public SlimeWorld readFromData(v1_9SlimeWorld data) { slimeChunk.tileEntities, slimeChunk.entities, new HashMap<>(), - slimeChunk.upgradeData + slimeChunk.upgradeData, + null, + null, + null )); } diff --git a/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java index 7f731fb2..aa2a32fd 100644 --- a/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java @@ -8,12 +8,9 @@ import com.infernalsuite.asp.api.world.SlimeWorld; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import net.kyori.adventure.nbt.CompoundBinaryTag; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class SkeletonCloning { @@ -76,7 +73,10 @@ private static Long2ObjectMap cloneChunkStorage(Collection(chunk.getTileEntities()), //No need to copy contents since adventure nbt is immutable new ArrayList<>(chunk.getEntities()), //No need to copy contents since adventure nbt is immutable new ConcurrentHashMap<>(chunk.getExtraData()), - null + null, + chunk.getPoiChunkSections(), + chunk.getBlockTicks(), + chunk.getFluidTicks() )); } diff --git a/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java index 00268e96..2f2e532c 100644 --- a/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java @@ -4,6 +4,7 @@ import com.infernalsuite.asp.api.world.SlimeChunkSection; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import java.util.List; import java.util.Map; @@ -13,7 +14,10 @@ public record SlimeChunkSkeleton(int x, int z, SlimeChunkSection[] sections, List blockEntities, List entities, Map extra, - CompoundBinaryTag upgradeData) implements SlimeChunk { + CompoundBinaryTag upgradeData, + CompoundBinaryTag poiChunk, + ListBinaryTag blockTicks, + ListBinaryTag fluidTicks) implements SlimeChunk { @Override public int getX() { @@ -54,4 +58,19 @@ public Map getExtraData() { public CompoundBinaryTag getUpgradeData() { return this.upgradeData; } + + @Override + public ListBinaryTag getBlockTicks() { + return this.blockTicks; + } + + @Override + public ListBinaryTag getFluidTicks() { + return this.fluidTicks; + } + + @Override + public CompoundBinaryTag getPoiChunkSections() { + return this.poiChunk; + } } diff --git a/gradle.properties b/gradle.properties index e002deb4..0da9d4f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ group=com.infernalsuite.asp -apiVersion=4.0.0 -version=1.21.4-R0.1-SNAPSHOT +apiVersion=4.1.0 +version=1.21.7-R0.1-SNAPSHOT -mcVersion=1.21.4 -paperRef=a838a886dcbc93664283034a41673e802a6b3098 +mcVersion=1.21.7 +paperRef=9686f8b34d552ca65da663d2e39454e469cda765 org.gradle.caching=true org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8662e158..d1ab5a72 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -adventure = "4.17.0" +adventure = "4.23.0" annotations = "26.0.1" autoservice = "1.1.1" blossom = "2.1.0" @@ -15,7 +15,7 @@ lettuce = "6.5.1.RELEASE" lombok = "1.18.36" lombok-plugin = "8.11" mongo = "5.2.1" -paperweight = "2.0.0-beta.14" +paperweight = "2.0.0-beta.17" plugin-yml-paper = "0.6.0" shadow = "8.3.5" slf4j = "2.0.16" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793..002b867c 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.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java b/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java index adf9383b..546a42ce 100644 --- a/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java +++ b/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java @@ -4,6 +4,7 @@ import com.infernalsuite.asp.api.loaders.UpdatableLoader; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import org.jetbrains.annotations.ApiStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,18 +66,13 @@ public MysqlLoader(String sqlURL, String host, int port, String database, boolea hikariConfig.addDataSourceProperty("maintainTimeStats", "false"); source = new HikariDataSource(hikariConfig); + init(); + } - try (Connection con = source.getConnection()) { - // Create worlds table - try (PreparedStatement statement = con.prepareStatement(CREATE_WORLDS_TABLE_QUERY)) { - statement.execute(); - } - - // Create versioning table - try (PreparedStatement statement = con.prepareStatement(CREATE_VERSIONING_TABLE_QUERY)) { - statement.execute(); - } - } + @ApiStatus.Experimental + public MysqlLoader(HikariDataSource hikariDataSource) throws SQLException { + source = hikariDataSource; + init(); } @Override @@ -199,4 +195,18 @@ public void deleteWorld(String worldName) throws IOException, UnknownWorldExcept } } + private void init() throws SQLException { + try (Connection con = source.getConnection()) { + // Create worlds table + try (PreparedStatement statement = con.prepareStatement(CREATE_WORLDS_TABLE_QUERY)) { + statement.execute(); + } + + // Create versioning table + try (PreparedStatement statement = con.prepareStatement(CREATE_VERSIONING_TABLE_QUERY)) { + statement.execute(); + } + } + } + } diff --git a/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java index 7f85212f..43cd2e39 100644 --- a/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java @@ -117,6 +117,8 @@ public void onDisable() { } Bukkit.unloadWorld(world.getName(), false); //Unload without saving as we have just saved (if not read only) } + + Bukkit.getServicesManager().unregisterAll(this); } private List loadWorlds() { diff --git a/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java index b1634d86..d72b5f8f 100644 --- a/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java @@ -3,39 +3,51 @@ import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.plugin.commands.CommandManager; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader; +import com.infernalsuite.asp.plugin.config.ConfigManager; +import com.infernalsuite.asp.plugin.config.WorldData; +import com.infernalsuite.asp.plugin.config.WorldsConfig; +import com.infernalsuite.asp.plugin.util.ExecutorUtil; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.block.Biome; import org.bukkit.command.CommandSender; -import org.incendo.cloud.annotations.Argument; -import org.incendo.cloud.annotations.Command; -import org.incendo.cloud.annotations.CommandDescription; -import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.*; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.CompletableFuture; -public class CreateWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { +public class CreateWorldCmd extends SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(CreateWorldCmd.class); - public CreateWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { + public CreateWorldCmd(CommandManager commandManager) { super(commandManager); } @Command("swp|aswm|swm create ") @CommandDescription("Create an empty world") @Permission("swm.createworld") - public CompletableFuture createWorld(CommandSender sender, @Argument(value = "world") String worldName, - @Argument(value = "data-source") com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader loader) { - + public CompletableFuture createWorld( + CommandSender sender, + @Argument(value = "world") String worldName, + @Argument(value = "data-source") NamedSlimeLoader loader, + @Flag(value = "biome") @Nullable NamespacedKey biome, + @Flag(value = "environment") @Nullable String environment + ) { if (commandManager.getWorldsInUse().contains(worldName)) { - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already being used on another command! Wait some time and try again.")).color(NamedTextColor.RED) ); } @@ -43,19 +55,35 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value World world = Bukkit.getWorld(worldName); if (world != null) { - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " already exists!")).color(NamedTextColor.RED) ); } - com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); + WorldsConfig config = ConfigManager.getWorldConfig(); if (config.getWorlds().containsKey(worldName)) { - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("There is already a world called " + worldName + " inside the worlds config file.")).color(NamedTextColor.RED) ); } + NamespacedKey defaultBiome = Objects.requireNonNull(NamespacedKey.fromString(SlimeProperties.DEFAULT_BIOME.getDefaultValue())); + Biome actualBiome = RegistryAccess.registryAccess().getRegistry(RegistryKey.BIOME) + .get(biome == null ? defaultBiome : biome); + + if(actualBiome == null) { + throw new MessageCommandException(COMMAND_PREFIX.append( + Component.text("Biome " + biome + "does not exist")).color(NamedTextColor.RED) + ); + } + + if(environment != null && !SlimeProperties.ENVIRONMENT.applyValidator(environment)) { + throw new MessageCommandException(COMMAND_PREFIX.append( + Component.text("Environment " + environment + " is not a valid environment. Valid options are: normal, nether, the_end")).color(NamedTextColor.RED) + ); + } + commandManager.getWorldsInUse().add(worldName); sender.sendMessage(COMMAND_PREFIX.append( Component.text("Creating empty world ").color(NamedTextColor.GRAY) @@ -73,15 +101,19 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value throw new WorldAlreadyExistsException("World already exists"); } - com.infernalsuite.asp.plugin.config.WorldData worldData = new com.infernalsuite.asp.plugin.config.WorldData(); + WorldData worldData = new WorldData(); worldData.setSpawn("0, 64, 0"); worldData.setDataSource(loader.name()); + worldData.setDefaultBiome(actualBiome.key().asString()); + if(environment != null) { + worldData.setEnvironment(environment); + } SlimePropertyMap propertyMap = worldData.toPropertyMap(); SlimeWorld slimeWorld = asp.createEmptyWorld(worldName, false, propertyMap, loader.slimeLoader()); asp.saveWorld(slimeWorld); - com.infernalsuite.asp.plugin.util.ExecutorUtil.runSyncAndWait(plugin, () -> { + ExecutorUtil.runSyncAndWait(plugin, () -> { try { asp.loadWorld(slimeWorld, true); @@ -92,7 +124,7 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value // Config config.getWorlds().put(worldName, worldData); } catch (IllegalArgumentException ex) { - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to create world " + worldName + ": " + ex.getMessage() + ".").color(NamedTextColor.RED) )); } @@ -105,12 +137,12 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value .append(Component.text(" created in " + (System.currentTimeMillis() - start) + "ms!").color(NamedTextColor.GREEN)) )); } catch (WorldAlreadyExistsException ex) { - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to create world " + worldName + ": world already exists (using data source '" + loader.name() + "').").color(NamedTextColor.RED) )); } catch (IOException ex) { LOGGER.error("Failed to create world {}:", worldName, ex); - throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( + throw new MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to create world " + worldName + ". Take a look at the server console for more information.").color(NamedTextColor.RED) )); } finally { diff --git a/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java index 6ce08da9..5a84322d 100644 --- a/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java @@ -1,5 +1,6 @@ package com.infernalsuite.asp.plugin.config; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import org.bukkit.Difficulty; import org.bukkit.World; @@ -43,6 +44,16 @@ public class WorldData { @Setting("readOnly") private boolean readOnly = false; + @Setting("saveBlockTicks") + private boolean saveBlockTicks = false; + @Setting("saveFluidTicks") + private boolean saveFluidTicks = false; + @Setting("savePoi") + private boolean savePoi = false; + + @Setting("seaLevel") + private int seaLevel = SEA_LEVEL.getDefaultValue(); + public SlimePropertyMap toPropertyMap() { try { Enum.valueOf(Difficulty.class, this.difficulty.toUpperCase()); @@ -94,7 +105,10 @@ public SlimePropertyMap toPropertyMap() { propertyMap.setValue(ENVIRONMENT, environment); propertyMap.setValue(WORLD_TYPE, worldType); propertyMap.setValue(DEFAULT_BIOME, defaultBiome); - + propertyMap.setValue(SAVE_BLOCK_TICKS, saveBlockTicks); + propertyMap.setValue(SAVE_FLUID_TICKS, saveFluidTicks); + propertyMap.setValue(SAVE_POI, savePoi); + propertyMap.setValue(SEA_LEVEL, seaLevel); return propertyMap; } diff --git a/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java index ccf09ad7..fd14d900 100644 --- a/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java @@ -7,8 +7,11 @@ import com.infernalsuite.asp.loaders.mongo.MongoLoader; import com.infernalsuite.asp.loaders.mysql.MysqlLoader; import com.infernalsuite.asp.loaders.redis.RedisLoader; +import com.infernalsuite.asp.plugin.SWPlugin; import com.mongodb.MongoException; import io.lettuce.core.RedisException; +import org.bukkit.Bukkit; +import org.bukkit.plugin.ServicePriority; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,18 +32,18 @@ public LoaderManager() { // File loader com.infernalsuite.asp.plugin.config.DatasourcesConfig.FileConfig fileConfig = config.getFileConfig(); - registerLoader("file", new FileLoader(new File(fileConfig.getPath()))); + registerLoader("file", registerLoaderService(FileLoader.class, new FileLoader(new File(fileConfig.getPath())))); // Mysql loader com.infernalsuite.asp.plugin.config.DatasourcesConfig.MysqlConfig mysqlConfig = config.getMysqlConfig(); if (mysqlConfig.isEnabled()) { try { - registerLoader("mysql", new MysqlLoader( + registerLoader("mysql", registerLoaderService(MysqlLoader.class, new MysqlLoader( mysqlConfig.getSqlUrl(), mysqlConfig.getHost(), mysqlConfig.getPort(), mysqlConfig.getDatabase(), mysqlConfig.isUsessl(), mysqlConfig.getUsername(), mysqlConfig.getPassword() - )); + ))); } catch (final SQLException ex) { LOGGER.error("Failed to establish connection to the MySQL server:", ex); } @@ -51,7 +54,7 @@ public LoaderManager() { if (mongoConfig.isEnabled()) { try { - registerLoader("mongodb", new MongoLoader( + registerLoader("mongodb", registerLoaderService(MongoLoader.class, new MongoLoader( mongoConfig.getDatabase(), mongoConfig.getCollection(), mongoConfig.getUsername(), @@ -60,7 +63,7 @@ public LoaderManager() { mongoConfig.getHost(), mongoConfig.getPort(), mongoConfig.getUri() - )); + ))); } catch (final MongoException ex) { LOGGER.error("Failed to establish connection to the MongoDB server:", ex); } @@ -69,7 +72,7 @@ public LoaderManager() { com.infernalsuite.asp.plugin.config.DatasourcesConfig.RedisConfig redisConfig = config.getRedisConfig(); if (redisConfig.isEnabled()){ try { - registerLoader("redis", new RedisLoader(redisConfig.getUri())); + registerLoader("redis", registerLoaderService(RedisLoader.class, new RedisLoader(redisConfig.getUri()))); } catch (final RedisException ex) { LOGGER.error("Failed to establish connection to the Redis server:", ex); } @@ -77,15 +80,20 @@ public LoaderManager() { com.infernalsuite.asp.plugin.config.DatasourcesConfig.APIConfig apiConfig = config.getApiConfig(); if(apiConfig.isEnabled()){ - registerLoader("api", new APILoader( + registerLoader("api", registerLoaderService(APILoader.class, new APILoader( apiConfig.getUrl(), apiConfig.getUsername(), apiConfig.getToken(), apiConfig.isIgnoreSslCertificate() - )); + ))); } } + private T registerLoaderService(Class clazz, T instance) { + Bukkit.getServicesManager().register(clazz, instance, SWPlugin.getInstance(), ServicePriority.Normal); + return instance; + } + public void registerLoader(String dataSource, SlimeLoader loader) { if (loaders.containsKey(dataSource)) { throw new IllegalArgumentException("Data source " + dataSource + " already has a declared loader!"); diff --git a/plugin/src/main/resources/worlds.yml b/plugin/src/main/resources/worlds.yml index 7d96db99..3bdec03e 100644 --- a/plugin/src/main/resources/worlds.yml +++ b/plugin/src/main/resources/worlds.yml @@ -10,6 +10,9 @@ # allowAnimals: false # loadOnStartup: true # readOnly: true +# saveBlockTicks: false +# saveFluidTicks: false +# savePoi: false # world2: # source: mysql # difficulty: hard @@ -18,6 +21,9 @@ # allowAnimals: false # loadOnStartup: false # readOnly: true +# saveBlockTicks: false +# saveFluidTicks: false +# savePoi: false # world3: # source: seaweed # difficulty: easy @@ -26,6 +32,9 @@ # allowAnimals: true # loadOnStartup: true # readOnly: false +# saveBlockTicks: false +# saveFluidTicks: false +# savePoi: false # world4: # source: api # difficulty: easy @@ -33,4 +42,7 @@ # allowMonsters: false # allowAnimals: true # loadOnStartup: true +# saveBlockTicks: false +# saveFluidTicks: false +# savePoi: false worlds: \ No newline at end of file