diff --git a/.gitignore b/.gitignore index dd9079f27..56d01e2ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,17 @@ -*.iml -*.ipr -*.iws .idea/ -.classpath -.project -.settings/ -slimeworldmanager-test/ -.DS_Store/ -release.properties -libs/ -.gradle/ +.vscode/ + +**/.gradle/ +.DS_Store build/ -slimeworldmanager-api -slimeworldmanager-server -run -*.hprof -paper-api-generator \ No newline at end of file +**/run/ + +**/*.iml + +aspaper-server/build.gradle.kts +aspaper-server/src/minecraft/ +paper-server/ +aspaper-api/build.gradle.kts +paper-api/ +paper-api-generator/ +aspaper-server/.gradle/ diff --git a/README.md b/README.md index 7cbb1572f..96dae0ae9 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ builds can be found in the [Discord](https://discord.gg/YevvsMa) under the #aspa ## Wiki For API and usage info check out the docs at https://infernalsuite.com/ -**If you run into any Flow-NBT errors when building your project, add the additional repository: `https://repo.rapture.pw/repository/maven-releases/`** - ## Javadocs The Javadocs can be found [here](https://docs.infernalsuite.com/). diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 6b52c8aea..0da106cd7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,96 +1,16 @@ plugins { - `java-library` - `maven-publish` - signing + id("asp.base-conventions") + id("asp.publishing-conventions") } dependencies { - api("com.flowpowered:flow-nbt:2.0.2") - api("org.jetbrains:annotations:23.0.0") + api(libs.annotations) + api(libs.adventure.nbt) - compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") + compileOnly(paperApi()) } -version = "3.0.0-SNAPSHOT" - -java { - withSourcesJar() - withJavadocJar() -} - -profiles { - profile("publish") { - activation { - property { - setKey("publish") - setValue("true") - } - } - action { - publishing { - publications { - create("maven") { - groupId = "${project.group}" - artifactId = project.name - version = "${project.version}" - - from(components["java"]) - - pom { - name.set("Advanced Slime Paper API") - description.set("API for ASP") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper") - licenses { - license { - name.set("GNU General Public License, Version 3.0") - url.set("https://www.gnu.org/licenses/gpl-3.0.txt") - } - } - developers { - developer { - id.set("InfernalSuite") - name.set("The InfernalSuite Team") - url.set("https://github.com/InfernalSuite") - email.set("infernalsuite@gmail.com") - } - } - scm { - connection.set("scm:git:https://github.com:InfernalSuite/AdvancedSlimePaper.git") - developerConnection.set("scm:git:ssh://github.com:InfernalSuite/AdvancedSlimePaper.git") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/") - } - issueManagement { - system.set("Github") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/issues") - } - } - - versionMapping { - usage("java-api") { - fromResolutionOf("runtimeClasspath") - } - usage("java-runtime") { - fromResolutionResult() - } - } - } - } - repositories { - maven { - name = "infernalsuite" - url = uri("https://repo.infernalsuite.com/repository/maven-snapshots/") - credentials { - username = project.property("ISUsername") as String? - password = project.property("ISPassword") as String? - } - } - } - } - - signing { - useGpgCmd() - sign(publishing.publications["maven"]) - } - } - } +publishConfiguration { + name = "Advanced Slime Paper API" + description = "API for Advanced Slime Paper" } diff --git a/api/src/main/java/com/infernalsuite/aswm/api/AdvancedSlimePaperAPI.java b/api/src/main/java/com/infernalsuite/asp/api/AdvancedSlimePaperAPI.java similarity index 80% rename from api/src/main/java/com/infernalsuite/aswm/api/AdvancedSlimePaperAPI.java rename to api/src/main/java/com/infernalsuite/asp/api/AdvancedSlimePaperAPI.java index 88d36977c..3125c48e6 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/AdvancedSlimePaperAPI.java +++ b/api/src/main/java/com/infernalsuite/asp/api/AdvancedSlimePaperAPI.java @@ -1,9 +1,17 @@ -package com.infernalsuite.aswm.api; +package com.infernalsuite.asp.api; -import com.infernalsuite.aswm.api.exceptions.*; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.InvalidWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; +import com.infernalsuite.asp.api.exceptions.WorldLoadedException; +import com.infernalsuite.asp.api.exceptions.WorldTooBigException; +import com.infernalsuite.asp.api.loaders.SlimeSerializationAdapter; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.api.loaders.SlimeLoader; import net.kyori.adventure.util.Services; import org.bukkit.World; import org.jetbrains.annotations.ApiStatus; @@ -44,7 +52,7 @@ SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, Sli * @param worldName the name of the world to get * @return the loaded world, or {@code null} if no loaded world matches the given name */ - SlimeWorld getLoadedWorld(String worldName); + SlimeWorldInstance getLoadedWorld(String worldName); /** * Gets a list of worlds which have been loaded by ASWM. @@ -52,7 +60,7 @@ SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, Sli * * @return a list of worlds */ - List getLoadedWorlds(); + List getLoadedWorlds(); /** * Generates a Minecraft World from a {@link SlimeWorld} and @@ -60,12 +68,12 @@ SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, Sli *
* This method must be called in sync with the Server Thread * - * @param world {@link SlimeWorld} world to be added to the server's world list + * @param world {@link SlimeWorldInstance} world to be added to the server's world list * @param callWorldLoadEvent Whether to call {@link org.bukkit.event.world.WorldLoadEvent} * @throws IllegalArgumentException if the world is already loaded * @return Returns a slime world representing a live minecraft world */ - SlimeWorld loadWorld(SlimeWorld world, boolean callWorldLoadEvent) throws IllegalArgumentException; + SlimeWorldInstance loadWorld(SlimeWorld world, boolean callWorldLoadEvent) throws IllegalArgumentException; /** * Checks if a {@link SlimeWorld} is loaded on the server. @@ -126,6 +134,17 @@ SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, Sli */ SlimeWorld readVanillaWorld(File worldDir, String worldName, @Nullable SlimeLoader loader) throws InvalidWorldException, WorldLoadedException, WorldTooBigException, IOException, WorldAlreadyExistsException; + /** + * Returns the {@link SlimeSerializationAdapter} used to serialize and deserialize SlimeWorlds. + * Manual de-/serialization is considered experimental and may change in future versions. + *

+ * Please use SlimeLoaders where possible + * + * @return A adapter for serializing and deserializing SlimeWorlds + */ + @ApiStatus.Experimental + SlimeSerializationAdapter getSerializer(); + /** * Gets the instance of the AdvancedSlimePaper API. * diff --git a/api/src/main/java/com/infernalsuite/asp/api/SlimeDataConverter.java b/api/src/main/java/com/infernalsuite/asp/api/SlimeDataConverter.java new file mode 100644 index 000000000..5820073d9 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/SlimeDataConverter.java @@ -0,0 +1,22 @@ +package com.infernalsuite.asp.api; + +import com.infernalsuite.asp.api.world.SlimeWorld; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; + +@ApiStatus.Internal +public interface SlimeDataConverter { + + // Will return new (fixed) instance + SlimeWorld applyDataFixers(SlimeWorld world); + + CompoundBinaryTag convertChunkTo1_13(CompoundBinaryTag globalTag); + + List convertEntities(List input, int from, int to); + List convertTileEntities(List input, int from, int to); + ListBinaryTag convertBlockPalette(ListBinaryTag input, int from, int to); + +} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/SlimeNMSBridge.java b/api/src/main/java/com/infernalsuite/asp/api/SlimeNMSBridge.java similarity index 66% rename from api/src/main/java/com/infernalsuite/aswm/api/SlimeNMSBridge.java rename to api/src/main/java/com/infernalsuite/asp/api/SlimeNMSBridge.java index 9aef6612d..81ab7450e 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/SlimeNMSBridge.java +++ b/api/src/main/java/com/infernalsuite/asp/api/SlimeNMSBridge.java @@ -1,15 +1,16 @@ -package com.infernalsuite.aswm.api; +package com.infernalsuite.asp.api; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import net.kyori.adventure.util.Services; import org.bukkit.World; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.ApiStatus; import java.io.IOException; +import java.util.List; @ApiStatus.Internal public interface SlimeNMSBridge { @@ -27,24 +28,18 @@ public interface SlimeNMSBridge { SlimeWorldInstance getInstance(World world); - // Will return new (fixed) instance - SlimeWorld applyDataFixers(SlimeWorld world); - int getCurrentVersion(); static SlimeNMSBridge instance() { return Holder.INSTANCE; } - void extractCraftPDC(PersistentDataContainer source, CompoundMap target); + void extractCraftPDC(PersistentDataContainer source, CompoundBinaryTag.Builder builder); - PersistentDataContainer extractCompoundMapIntoCraftPDC(CompoundMap source); + SlimeDataConverter getSlimeDataConverter(); @ApiStatus.Internal class Holder { private static final SlimeNMSBridge INSTANCE = Services.service(SlimeNMSBridge.class).orElseThrow(); } - - CompoundTag convertChunkTo1_13(CompoundTag tag); - } diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/LoadSlimeWorldEvent.java b/api/src/main/java/com/infernalsuite/asp/api/events/LoadSlimeWorldEvent.java similarity index 68% rename from api/src/main/java/com/infernalsuite/aswm/api/events/LoadSlimeWorldEvent.java rename to api/src/main/java/com/infernalsuite/asp/api/events/LoadSlimeWorldEvent.java index 7f8f144a2..64690b9c1 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/LoadSlimeWorldEvent.java +++ b/api/src/main/java/com/infernalsuite/asp/api/events/LoadSlimeWorldEvent.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.api.events; +package com.infernalsuite.asp.api.events; -import com.infernalsuite.aswm.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; @@ -10,9 +10,9 @@ public class LoadSlimeWorldEvent extends Event { private static final HandlerList handlers = new HandlerList(); - private final SlimeWorld slimeWorld; + private final SlimeWorldInstance slimeWorld; - public LoadSlimeWorldEvent(SlimeWorld slimeWorld) { + public LoadSlimeWorldEvent(SlimeWorldInstance slimeWorld) { super(false); this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); } @@ -26,7 +26,7 @@ public static HandlerList getHandlerList() { return handlers; } - public SlimeWorld getSlimeWorld() { + public SlimeWorldInstance getSlimeWorld() { return slimeWorld; } } diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/CorruptedWorldException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/CorruptedWorldException.java similarity index 88% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/CorruptedWorldException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/CorruptedWorldException.java index 7b29bf585..2d5f491f3 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/CorruptedWorldException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/CorruptedWorldException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a world could not diff --git a/api/src/main/java/com/infernalsuite/asp/api/exceptions/InvalidWorldException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/InvalidWorldException.java new file mode 100644 index 000000000..a9d40f190 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/InvalidWorldException.java @@ -0,0 +1,28 @@ +package com.infernalsuite.asp.api.exceptions; + +import java.io.File; +import java.nio.file.Path; + +/** + * Exception thrown when a folder does + * not contain a valid Minecraft world. + */ +public class InvalidWorldException extends SlimeException { + + public InvalidWorldException(Path worldDir, String reason) { + super("Directory " + worldDir.toString() + " does not contain a valid MC world! " + reason); + } + + public InvalidWorldException(Path worldDir) { + super("Directory " + worldDir.toString() + " does not contain a valid MC world!"); + } + + public static InvalidWorldException legacy(File worldDir, String reason) { + return new InvalidWorldException(worldDir.toPath(), reason); + } + + public static InvalidWorldException legacy(File worldDir) { + return new InvalidWorldException(worldDir.toPath()); + } + +} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/NewerFormatException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/NewerFormatException.java similarity index 84% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/NewerFormatException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/NewerFormatException.java index dd54386ee..e29eb5521 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/NewerFormatException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/NewerFormatException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a world is encoded diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/SlimeException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/SlimeException.java similarity index 84% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/SlimeException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/SlimeException.java index 545fcff0a..a7a4d976a 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/SlimeException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/SlimeException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Generic SWM exception. diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/UnknownWorldException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/UnknownWorldException.java similarity index 82% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/UnknownWorldException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/UnknownWorldException.java index 89499cdf2..e50ce81c4 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/UnknownWorldException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/UnknownWorldException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldAlreadyExistsException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldAlreadyExistsException.java similarity index 84% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldAlreadyExistsException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldAlreadyExistsException.java index 425eeab45..22c366573 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldAlreadyExistsException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldAlreadyExistsException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a world diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldLoadedException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldLoadedException.java similarity index 85% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldLoadedException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldLoadedException.java index 30a014458..646e9d355 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldLoadedException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldLoadedException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a world is loaded diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldTooBigException.java b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldTooBigException.java similarity index 86% rename from api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldTooBigException.java rename to api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldTooBigException.java index 913b74864..a35c42553 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/WorldTooBigException.java +++ b/api/src/main/java/com/infernalsuite/asp/api/exceptions/WorldTooBigException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.exceptions; +package com.infernalsuite.asp.api.exceptions; /** * Exception thrown when a MC world is diff --git a/api/src/main/java/com/infernalsuite/aswm/api/loaders/SlimeLoader.java b/api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeLoader.java similarity index 94% rename from api/src/main/java/com/infernalsuite/aswm/api/loaders/SlimeLoader.java rename to api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeLoader.java index 722a63f41..6cc1ff276 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/loaders/SlimeLoader.java +++ b/api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeLoader.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.api.loaders; +package com.infernalsuite.asp.api.loaders; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; import java.io.IOException; import java.util.List; diff --git a/api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeSerializationAdapter.java b/api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeSerializationAdapter.java new file mode 100644 index 000000000..7ca000585 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/loaders/SlimeSerializationAdapter.java @@ -0,0 +1,56 @@ +package com.infernalsuite.asp.api.loaders; + +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +@ApiStatus.Experimental +public interface SlimeSerializationAdapter { + + /** + * Serializes a {@link SlimeWorld} with the current format {@link SlimeSerializationAdapter#getSlimeFormat()}. + *

+ * If the world is loaded, make sure to use a serializable copy that can be obtained with {@link SlimeWorldInstance#getSerializableCopy()}. + * + * @param slimeWorld World to serialize. + * @return Serialized world data in bytes. + * @throws IllegalArgumentException If the world is a {@link SlimeWorldInstance} + */ + byte[] serializeWorld(@NotNull SlimeWorld slimeWorld); + + /** + * Deserializes a world from the given byte array and applies data fixers to the world. + *

+ * Unlike {@link AdvancedSlimePaperAPI#readWorld(SlimeLoader, String, boolean, SlimePropertyMap)} this does not + * save data fixed worlds to the provided {@link SlimeLoader} automatically. + * + * @param worldName Name of the world. + * @param serializedWorld Serialized world data in bytes. + * @param loader {@link SlimeLoader} used when saving the world. + * @param propertyMap A {@link SlimePropertyMap} object containing all the properties of the world. + * @param readOnly Whether read-only mode is enabled. + * @return A {@link SlimeWorld}, which is the in-memory representation of the world. + * @throws IOException if there was a generic problem reading the world. + * @throws CorruptedWorldException if the world retrieved cannot be properly parsed into a {@link SlimeWorld} object. + * @throws NewerFormatException if the world uses a newer version of the SRF. + */ + @NotNull SlimeWorld deserializeWorld(@NotNull String worldName, byte[] serializedWorld, @Nullable SlimeLoader loader, + @NotNull SlimePropertyMap propertyMap, boolean readOnly) + throws CorruptedWorldException, NewerFormatException, IOException; + + /** + * The current slime format version used by AdvancedSlimePaper. + * + * @return The slime format version + */ + int getSlimeFormat(); + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/loaders/UpdatableLoader.java b/api/src/main/java/com/infernalsuite/asp/api/loaders/UpdatableLoader.java new file mode 100644 index 000000000..772769b38 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/loaders/UpdatableLoader.java @@ -0,0 +1,28 @@ +package com.infernalsuite.asp.api.loaders; + +import java.io.IOException; + +public abstract class UpdatableLoader implements SlimeLoader { + + public abstract void update() throws NewerStorageException, IOException; + + public static class NewerStorageException extends Exception { + + private final int implementationVersion; + private final int storageVersion; + + + public NewerStorageException(int implementationVersion, int storageVersion) { + this.implementationVersion = implementationVersion; + this.storageVersion = storageVersion; + } + + public int getImplementationVersion() { + return implementationVersion; + } + + public int getStorageVersion() { + return storageVersion; + } + } +} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/utils/NibbleArray.java b/api/src/main/java/com/infernalsuite/asp/api/utils/NibbleArray.java similarity index 96% rename from api/src/main/java/com/infernalsuite/aswm/api/utils/NibbleArray.java rename to api/src/main/java/com/infernalsuite/asp/api/utils/NibbleArray.java index 89419ce79..23318a82b 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/utils/NibbleArray.java +++ b/api/src/main/java/com/infernalsuite/asp/api/utils/NibbleArray.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.utils; +package com.infernalsuite.asp.api.utils; /** * Credits to Minikloon for this class. diff --git a/api/src/main/java/com/infernalsuite/aswm/api/utils/SlimeFormat.java b/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java similarity index 87% rename from api/src/main/java/com/infernalsuite/aswm/api/utils/SlimeFormat.java rename to api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java index d41e44779..4ada699f3 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/utils/SlimeFormat.java +++ b/api/src/main/java/com/infernalsuite/asp/api/utils/SlimeFormat.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.api.utils; +package com.infernalsuite.asp.api.utils; /** * Class containing some standards of the SRF. diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunk.java b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java similarity index 51% rename from api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunk.java rename to api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java index 770f79e8e..b6d3a0b48 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunk.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunk.java @@ -1,8 +1,10 @@ -package com.infernalsuite.aswm.api.world; +package com.infernalsuite.asp.api.world; -import com.flowpowered.nbt.CompoundTag; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import java.util.List; +import java.util.Map; import javax.annotation.Nullable; /** @@ -32,46 +34,43 @@ public interface SlimeChunk { SlimeChunkSection[] getSections(); /** - * Returns the height maps of the chunk. If it's a pre 1.13 world, - * a {@link com.flowpowered.nbt.IntArrayTag} containing the height - * map will be stored inside here by the name of 'heightMap'. + * Returns the height maps of the chunk. * - * @return A {@link CompoundTag} containing all the height maps of the chunk. + * @return A {@link CompoundBinaryTag} containing all the height maps of the chunk. */ - CompoundTag getHeightMaps(); + CompoundBinaryTag getHeightMaps(); /** * Returns all the tile entities of the chunk. * - * @return A {@link CompoundTag} containing all the tile entities of the chunk. + * @return A {@link CompoundBinaryTag} containing all the tile entities of the chunk. */ - List getTileEntities(); + List getTileEntities(); /** * Returns all the entities of the chunk. * - * @return A {@link CompoundTag} containing all the entities + * @return A {@link CompoundBinaryTag} containing all the entities */ - List getEntities(); + List getEntities(); /** * Returns the extra data of the chunk. - * Inside this {@link CompoundTag} - * can be stored any information to then be retrieved later, as it's - * saved alongside the chunk data. + * Any information can be stored in this {@link Map} + * and later retrieved, as it's saved alongside the chunk data. *
* Beware, a compound tag under the key "ChunkBukkitValues" will be stored here. * It is used for storing chunk-based Bukkit PDC. Do not overwrite it. * - * @return A {@link CompoundTag} containing the extra data of the chunk, + * @return A {@link Map} containing the extra data of the chunk as NBT tags, */ - CompoundTag getExtraData(); + Map getExtraData(); /** * Upgrade data used to fix the chunks. * Not intended to be serialized. - * @return A {@link CompoundTag} containing the upgrade data of the chunk, + * @return A {@link CompoundBinaryTag} containing the upgrade data of the chunk, */ @Nullable - CompoundTag getUpgradeData(); + CompoundBinaryTag getUpgradeData(); } diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunkSection.java b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunkSection.java similarity index 68% rename from api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunkSection.java rename to api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunkSection.java index 53b445086..12dc93413 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunkSection.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeChunkSection.java @@ -1,7 +1,7 @@ -package com.infernalsuite.aswm.api.world; +package com.infernalsuite.asp.api.world; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.flowpowered.nbt.CompoundTag; +import com.infernalsuite.asp.api.utils.NibbleArray; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.Nullable; /** @@ -9,9 +9,9 @@ */ public interface SlimeChunkSection { - CompoundTag getBlockStatesTag(); + CompoundBinaryTag getBlockStatesTag(); - CompoundTag getBiomeTag(); + CompoundBinaryTag getBiomeTag(); /** * Returns the block light data. diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorld.java b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorld.java similarity index 72% rename from api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorld.java rename to api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorld.java index c86fd9dde..0fce84fae 100644 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorld.java +++ b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorld.java @@ -1,13 +1,16 @@ -package com.infernalsuite.aswm.api.world; +package com.infernalsuite.asp.api.world; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.bukkit.persistence.PersistentDataHolder; import java.io.IOException; import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; /** * In-memory representation of a SRF world. @@ -42,21 +45,25 @@ public interface SlimeWorld extends PersistentDataHolder { Collection getChunkStorage(); /** - * Returns the extra data of the world. Inside this {@link CompoundTag} - * can be stored any information to then be retrieved later, as it's - * saved alongside the world data. + * Extra data to be stored alongside the world. * - * @return A {@link CompoundTag} containing the extra data of the world. + *

Any information can be stored inside this map, it will be serialized into a {@link CompoundBinaryTag} + * and stored alongside the world data so it can then be retrieved later.

+ * + * @apiNote There is a maximum limit of 512 nested tags + * @implSpec The returned map must be an implementation of {@link ConcurrentMap} to avoid CMEs, etc. + * + * @return A Map containing the extra data of the world. */ - CompoundTag getExtraData(); + ConcurrentMap getExtraData(); /** * Returns a {@link Collection} with every world map, serialized - * in a {@link CompoundTag} object. + * in a {@link CompoundBinaryTag} object. * * @return A {@link Collection} containing every world map. */ - Collection getWorldMaps(); + Collection getWorldMaps(); /** * Returns the property map. diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorldInstance.java b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorldInstance.java new file mode 100644 index 000000000..82927703c --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/SlimeWorldInstance.java @@ -0,0 +1,25 @@ +package com.infernalsuite.asp.api.world; + +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import net.kyori.adventure.nbt.BinaryTag; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ConcurrentMap; + +/* + * Represents a loaded SlimeWorld. This world is synchronized with the state of the bukkit world. + */ +public interface SlimeWorldInstance extends SlimeWorld { + + /** + * Returns the bukkit instance of the loaded world. + * + * @return The bukkit world. + */ + @NotNull World getBukkitWorld(); + + SlimeWorld getSerializableCopy(); + +} 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 new file mode 100644 index 000000000..90a56d53d --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperties.java @@ -0,0 +1,109 @@ +package com.infernalsuite.asp.api.world.properties; + +import com.infernalsuite.asp.api.world.properties.type.SlimePropertyBoolean; +import com.infernalsuite.asp.api.world.properties.type.SlimePropertyFloat; +import com.infernalsuite.asp.api.world.properties.type.SlimePropertyInt; +import com.infernalsuite.asp.api.world.properties.type.SlimePropertyString; +import org.jetbrains.annotations.ApiStatus; + +/** + * Class with all existing slime world properties. + */ +public class SlimeProperties { + + /** + * The X coordinate of the world spawn + */ + public static final SlimePropertyInt SPAWN_X = SlimePropertyInt.create("spawnX", 0); + + /** + * The Y coordinate of the world spawn + */ + public static final SlimePropertyInt SPAWN_Y = SlimePropertyInt.create("spawnY", 255); + + /** + * The Z coordinate of the world spawn + */ + public static final SlimePropertyInt SPAWN_Z = SlimePropertyInt.create("spawnZ", 0); + + /** + * The yaw of the world spawn + */ + public static final SlimePropertyFloat SPAWN_YAW = SlimePropertyFloat.create("spawnYaw", 0.0f); + + /** + * The difficulty set for the world + */ + public static final SlimePropertyString DIFFICULTY = SlimePropertyString.create("difficulty", "peaceful", (value) -> + value.equalsIgnoreCase("peaceful") || value.equalsIgnoreCase("easy") + || value.equalsIgnoreCase("normal") || value.equalsIgnoreCase("hard") + ); + + /** + * Whether monsters are allowed to spawn at night or in the dark + */ + public static final SlimePropertyBoolean ALLOW_MONSTERS = SlimePropertyBoolean.create("allowMonsters", true); + + /** + * Whether peaceful animals are allowed to spawn + */ + public static final SlimePropertyBoolean ALLOW_ANIMALS = SlimePropertyBoolean.create("allowAnimals", true); + + /** + * Whether the dragon battle should be enabled in end worlds + */ + public static final SlimePropertyBoolean DRAGON_BATTLE = SlimePropertyBoolean.create("dragonBattle", false); + + /** + * Whether PVP combat is allowed + */ + public static final SlimePropertyBoolean PVP = SlimePropertyBoolean.create("pvp", true); + + /** + * The environment of the world + */ + public static final SlimePropertyString ENVIRONMENT = SlimePropertyString.create("environment", "normal", (value) -> + value.equalsIgnoreCase("normal") || value.equalsIgnoreCase("nether") || value.equalsIgnoreCase("the_end") + ); + + /** + * The type of world + */ + public static final SlimePropertyString WORLD_TYPE = SlimePropertyString.create("worldtype", "default", (value) -> + value.equalsIgnoreCase("default") || value.equalsIgnoreCase("flat") || value.equalsIgnoreCase("large_biomes") + || value.equalsIgnoreCase("amplified") || value.equalsIgnoreCase("customized") + || value.equalsIgnoreCase("debug_all_block_states") || value.equalsIgnoreCase("default_1_1") + ); + + /** + * The default biome generated in empty chunks + */ + public static final SlimePropertyString DEFAULT_BIOME = SlimePropertyString.create("defaultBiome", "minecraft:plains"); + + @ApiStatus.Experimental + public static final SlimePropertyBoolean SHOULD_LIMIT_SAVE = SlimePropertyBoolean.create("hasSaveBounds", false); + + @ApiStatus.Experimental + public static final SlimePropertyInt SAVE_MIN_X = SlimePropertyInt.create("saveMinX", 0); + @ApiStatus.Experimental + public static final SlimePropertyInt SAVE_MIN_Z = SlimePropertyInt.create("saveMinZ", 0); + + @ApiStatus.Experimental + public static final SlimePropertyInt SAVE_MAX_X = SlimePropertyInt.create("saveMaxX", 0); + @ApiStatus.Experimental + public static final SlimePropertyInt SAVE_MAX_Z = SlimePropertyInt.create("saveMaxZ", 0); + + @ApiStatus.Experimental + public static final SlimePropertyString CHUNK_PRUNING = SlimePropertyString.create("pruning", "aggressive", (value) -> + 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); + + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperty.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperty.java new file mode 100644 index 000000000..b6bc8b185 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimeProperty.java @@ -0,0 +1,61 @@ +package com.infernalsuite.asp.api.world.properties; + +import net.kyori.adventure.nbt.BinaryTag; + +import java.util.function.Function; + +/** + * A property describing behavior of a slime world. + */ +public abstract class SlimeProperty { + + private final String key; + private final T defaultValue; + private final Function validator; + + protected SlimeProperty(String key, T defaultValue) { + this(key, defaultValue, null); + } + + protected SlimeProperty(String key, T defaultValue, Function validator) { + this.key = key; + + if (defaultValue != null && validator != null && !validator.apply(defaultValue)) { + throw new IllegalArgumentException("Invalid default value for property " + key + "! " + defaultValue); + } + + this.defaultValue = defaultValue; + this.validator = validator; + } + + protected abstract Z createTag(T value); + + protected abstract T readValue(Z tag); + + protected abstract Z cast(BinaryTag rawTag); + + public final boolean applyValidator(T value) { + return this.validator == null || this.validator.apply(value); + } + + public final String getKey() { + return this.key; + } + + public final T getDefaultValue() { + return this.defaultValue; + } + + public final Function getValidator() { + return this.validator; + } + + @Override + public final String toString() { + return "SlimeProperty{" + + "key='" + key + '\'' + + ", defaultValue=" + defaultValue + + '}'; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java new file mode 100644 index 000000000..1c687ee3f --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/SlimePropertyMap.java @@ -0,0 +1,95 @@ +package com.infernalsuite.asp.api.world.properties; + +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; + +import java.util.HashMap; +import java.util.Map; + +/** + * A Property Map object. + */ +public class SlimePropertyMap { + + private final Map properties; + + public SlimePropertyMap() { + this(new HashMap<>()); + } + + public SlimePropertyMap(final Map properties) { + this.properties = properties; + } + + /** + * Return the current value of the given property + * + * @param property The slime property + * @return The current value + */ + public T getValue(final SlimeProperty property) { + if (this.properties.containsKey(property.getKey())) { + return property.readValue(property.cast(this.properties.get(property.getKey()))); + } else { + return property.getDefaultValue(); + } + } + + /** + * Return the properties (CompoundMap) + * + * @return The properties + */ + public Map getProperties() { + return this.properties; + } + + /** + * Update the value of the given property + * + * @param property The slime property + * @param value The new value + * @throws IllegalArgumentException if the value fails validation. + */ + public void setValue(final SlimeProperty property, final T value) { + if (!property.applyValidator(value)) throw new IllegalArgumentException("'%s' is not a valid property value.".formatted(value)); + this.properties.put(property.getKey(), property.createTag(value)); + } + + /** + * Copies all values from the specified {@link SlimePropertyMap}. + * If the same property has different values on both maps, the one + * on the provided map will be used. + * + * @param other A {@link SlimePropertyMap}. + */ + public void merge(final SlimePropertyMap other) { + this.properties.putAll(other.properties); + } + + /** + * Returns a {@link CompoundBinaryTag} containing every property set in this map. + * + * @return A {@link CompoundBinaryTag} with all the properties stored in this map. + */ + public CompoundBinaryTag toCompound() { + return CompoundBinaryTag.builder().put(this.properties).build(); + } + + public static SlimePropertyMap fromCompound(final CompoundBinaryTag tag) { + final Map tags = new HashMap<>(tag.size()); + tag.forEach(entry -> tags.put(entry.getKey(), entry.getValue())); + return new SlimePropertyMap(tags); + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + public SlimePropertyMap clone() { + return new SlimePropertyMap(new HashMap<>(this.properties)); + } + + @Override + public String toString() { + return "SlimePropertyMap{" + properties + '}'; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyBoolean.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyBoolean.java new file mode 100644 index 000000000..c32f5af3e --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyBoolean.java @@ -0,0 +1,57 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.ByteBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * A slime property of type boolean + */ +public class SlimePropertyBoolean extends SlimeProperty { + + public static SlimePropertyBoolean create(final @NotNull String key, final boolean defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyBoolean(key, defaultValue); + } + + public static SlimePropertyBoolean create(final @NotNull String key, final boolean defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyBoolean#create(String, boolean) instead"); + return new SlimePropertyBoolean(key, defaultValue, validator); + } + + /** + * @deprecated Use {@link #create(String, boolean)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyBoolean(String key, Boolean defaultValue) { + super(key, defaultValue); + } + + /** + * @deprecated Use {@link #create(String, boolean, Function)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyBoolean(String key, Boolean defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected ByteBinaryTag createTag(final Boolean value) { + return value ? ByteBinaryTag.ONE : ByteBinaryTag.ZERO; + } + + @Override + protected Boolean readValue(final ByteBinaryTag tag) { + return tag.value() == 1; + } + + @Override + protected ByteBinaryTag cast(BinaryTag rawTag) { + return (ByteBinaryTag) rawTag; + } +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByte.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByte.java new file mode 100644 index 000000000..3640ee00e --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByte.java @@ -0,0 +1,47 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.ByteBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyByte extends SlimeProperty { + + public static SlimePropertyByte create(final @NotNull String key, final byte defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyByte(key, defaultValue); + } + + public static SlimePropertyByte create(final @NotNull String key, final byte defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyByte#create(String, byte) instead"); + return new SlimePropertyByte(key, defaultValue, validator); + } + + private SlimePropertyByte(String key, Byte defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyByte(String key, Byte defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected ByteBinaryTag createTag(final Byte value) { + return ByteBinaryTag.byteBinaryTag(value); + } + + @Override + protected Byte readValue(final ByteBinaryTag tag) { + return tag.value(); + } + + @Override + protected ByteBinaryTag cast(BinaryTag rawTag) { + return (ByteBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByteArray.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByteArray.java new file mode 100644 index 000000000..1f71d53f4 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyByteArray.java @@ -0,0 +1,48 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.ByteArrayBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyByteArray extends SlimeProperty { + + public static SlimePropertyByteArray create(final @NotNull String key, final byte[] defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyByteArray(key, defaultValue); + } + + public static SlimePropertyByteArray create(final @NotNull String key, final byte[] defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyByteArray#create(String, byte[]) instead"); + return new SlimePropertyByteArray(key, defaultValue, validator); + } + + private SlimePropertyByteArray(String key, byte[] defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyByteArray(String key, byte[] defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected ByteArrayBinaryTag createTag(final byte[] value) { + return ByteArrayBinaryTag.byteArrayBinaryTag(value); + } + + @Override + protected byte[] readValue(final ByteArrayBinaryTag tag) { + return tag.value(); + } + + @Override + protected ByteArrayBinaryTag cast(BinaryTag rawTag) { + return (ByteArrayBinaryTag) rawTag; + } + +} + diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyDouble.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyDouble.java new file mode 100644 index 000000000..ade361759 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyDouble.java @@ -0,0 +1,46 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.DoubleBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyDouble extends SlimeProperty { + + public static SlimePropertyDouble create(final @NotNull String key, final double defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyDouble(key, defaultValue); + } + + public static SlimePropertyDouble create(final @NotNull String key, final double defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyDouble#create(String, double) instead"); + return new SlimePropertyDouble(key, defaultValue, validator); + } + + private SlimePropertyDouble(String key, Double defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyDouble(String key, Double defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected DoubleBinaryTag createTag(final Double value) { + return DoubleBinaryTag.doubleBinaryTag(value); + } + + @Override + protected Double readValue(final DoubleBinaryTag tag) { + return tag.value(); + } + + @Override + protected DoubleBinaryTag cast(BinaryTag rawTag) { + return (DoubleBinaryTag) rawTag; + } +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyFloat.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyFloat.java new file mode 100644 index 000000000..239ff0c5e --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyFloat.java @@ -0,0 +1,58 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.FloatBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * A slime property of type float + */ +public class SlimePropertyFloat extends SlimeProperty { + + public static SlimePropertyFloat create(final @NotNull String key, final float defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyFloat(key, defaultValue); + } + + public static SlimePropertyFloat create(final @NotNull String key, final float defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyFloat#create(String, float) instead"); + return new SlimePropertyFloat(key, defaultValue, validator); + } + + /** + * @deprecated use {@link #create(String, float)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyFloat(String key, Float defaultValue) { + super(key, defaultValue); + } + + /** + * @deprecated use {@link #create(String, float, Function)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyFloat(String key, Float defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected FloatBinaryTag createTag(final Float value) { + return FloatBinaryTag.floatBinaryTag(value); + } + + @Override + protected Float readValue(final FloatBinaryTag tag) { + return tag.value(); + } + + @Override + protected FloatBinaryTag cast(BinaryTag rawTag) { + return (FloatBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyInt.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyInt.java new file mode 100644 index 000000000..4d13026f4 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyInt.java @@ -0,0 +1,58 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.IntBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * A slime property of type integer + */ +public class SlimePropertyInt extends SlimeProperty { + + public static SlimePropertyInt create(final @NotNull String key, final int defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyInt(key, defaultValue); + } + + public static SlimePropertyInt create(final @NotNull String key, final int defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyInt#create(String, int) instead"); + return new SlimePropertyInt(key, defaultValue, validator); + } + + /** + * @deprecated Use {@link #create(String, int)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyInt(String key, Integer defaultValue) { + super(key, defaultValue); + } + + /** + * @deprecated Use {@link #create(String, int, Function)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyInt(String key, Integer defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected IntBinaryTag createTag(final Integer value) { + return IntBinaryTag.intBinaryTag(value); + } + + @Override + protected Integer readValue(final IntBinaryTag tag) { + return tag.value(); + } + + @Override + protected IntBinaryTag cast(BinaryTag rawTag) { + return (IntBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyIntArray.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyIntArray.java new file mode 100644 index 000000000..e74cb6aa3 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyIntArray.java @@ -0,0 +1,47 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.IntArrayBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyIntArray extends SlimeProperty { + + public static SlimePropertyIntArray create(final @NotNull String key, final int[] defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyIntArray(key, defaultValue); + } + + public static SlimePropertyIntArray create(final @NotNull String key, final int[] defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyIntArray#create(String, int[]) instead"); + return new SlimePropertyIntArray(key, defaultValue, validator); + } + + private SlimePropertyIntArray(String key, int[] defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyIntArray(String key, int[] defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected IntArrayBinaryTag createTag(final int[] value) { + return IntArrayBinaryTag.intArrayBinaryTag(value); + } + + @Override + protected int[] readValue(final IntArrayBinaryTag tag) { + return tag.value(); + } + + @Override + protected IntArrayBinaryTag cast(BinaryTag rawTag) { + return (IntArrayBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyList.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyList.java new file mode 100644 index 000000000..02931bf4b --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyList.java @@ -0,0 +1,88 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.ListBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Function; + +public class SlimePropertyList extends SlimeProperty, ListBinaryTag> { + + public static SlimePropertyList create( + final @NotNull String key, + final @NotNull List defaultValue, + final @NotNull BinaryTagType listTagElementType, + final @NotNull Function elementTagConverter, + final @NotNull Function elementTagExtractor + ) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(defaultValue, "Default value cannot be null"); + Preconditions.checkNotNull(listTagElementType, "List tag element type cannot be null"); + Preconditions.checkNotNull(elementTagConverter, "Element tag converter cannot be null"); + Preconditions.checkNotNull(elementTagExtractor, "Element tag extractor cannot be null"); + return new SlimePropertyList<>(key, defaultValue, listTagElementType, elementTagConverter, elementTagExtractor); + } + + public static SlimePropertyList create( + final @NotNull String key, + final @NotNull List defaultValue, + final @NotNull Function, Boolean> validator, + final @NotNull BinaryTagType listTagElementType, + final @NotNull Function elementTagConverter, + final @NotNull Function elementTagExtractor + ) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(defaultValue, "Default value cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyList#create(String, List) instead"); + Preconditions.checkNotNull(listTagElementType, "List tag element type cannot be null"); + Preconditions.checkNotNull(elementTagConverter, "Element tag converter cannot be null"); + Preconditions.checkNotNull(elementTagExtractor, "Element tag extractor cannot be null"); + return new SlimePropertyList<>(key, defaultValue, validator, listTagElementType, elementTagConverter, elementTagExtractor); + } + + private final BinaryTagType listTagElementType; + private final Function elementTagConverter; + private final Function elementTagExtractor; + + private SlimePropertyList(String key, List defaultValue, BinaryTagType listTagElementType, Function elementTagConverter, Function elementTagExtractor) { + super(key, defaultValue); + this.listTagElementType = listTagElementType; + this.elementTagConverter = elementTagConverter; + this.elementTagExtractor = elementTagExtractor; + } + + private SlimePropertyList(String key, List defaultValue, Function, Boolean> validator, BinaryTagType listTagElementType, Function elementTagConverter, Function elementTagExtractor) { + super(key, defaultValue, validator); + this.listTagElementType = listTagElementType; + this.elementTagConverter = elementTagConverter; + this.elementTagExtractor = elementTagExtractor; + } + + @SuppressWarnings("unchecked") + @Override + protected ListBinaryTag createTag(final List value) { + return ListBinaryTag.listBinaryTag(this.listTagElementType, (List) value.stream() + .map(this.elementTagConverter) + .toList() + ); + } + + @SuppressWarnings("unchecked") + @Override + protected List readValue(final ListBinaryTag tag) { + return tag.stream() + .map(rawTag -> (Z) rawTag) + .map(this.elementTagExtractor) + .toList(); + } + + @Override + protected ListBinaryTag cast(final BinaryTag rawTag) { + return (ListBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLong.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLong.java new file mode 100644 index 000000000..31e0a1502 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLong.java @@ -0,0 +1,47 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.LongBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyLong extends SlimeProperty { + + public static SlimePropertyLong create(final @NotNull String key, final long defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyLong(key, defaultValue); + } + + public static SlimePropertyLong create(final @NotNull String key, final long defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyLong#create(String, long) instead"); + return new SlimePropertyLong(key, defaultValue, validator); + } + + private SlimePropertyLong(String key, Long defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyLong(String key, Long defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected LongBinaryTag createTag(final Long value) { + return LongBinaryTag.longBinaryTag(value); + } + + @Override + protected Long readValue(final LongBinaryTag tag) { + return tag.value(); + } + + @Override + protected LongBinaryTag cast(BinaryTag rawTag) { + return (LongBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLongArray.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLongArray.java new file mode 100644 index 000000000..46a9be665 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyLongArray.java @@ -0,0 +1,47 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.LongArrayBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyLongArray extends SlimeProperty { + + public static SlimePropertyLongArray create(final @NotNull String key, final long[] defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyLongArray(key, defaultValue); + } + + public static SlimePropertyLongArray create(final @NotNull String key, final long[] defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyLongArray#create(String, long[]) instead"); + return new SlimePropertyLongArray(key, defaultValue, validator); + } + + private SlimePropertyLongArray(String key, long[] defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyLongArray(String key, long[] defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected LongArrayBinaryTag createTag(final long[] value) { + return LongArrayBinaryTag.longArrayBinaryTag(value); + } + + @Override + protected long[] readValue(final LongArrayBinaryTag tag) { + return tag.value(); + } + + @Override + protected LongArrayBinaryTag cast(BinaryTag rawTag) { + return (LongArrayBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyShort.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyShort.java new file mode 100644 index 000000000..f34184488 --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyShort.java @@ -0,0 +1,47 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.ShortBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class SlimePropertyShort extends SlimeProperty { + + public static SlimePropertyShort create(final @NotNull String key, final short defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyShort(key, defaultValue); + } + + public static SlimePropertyShort create(final @NotNull String key, final short defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyShort#create(String, short) instead"); + return new SlimePropertyShort(key, defaultValue, validator); + } + + private SlimePropertyShort(String key, Short defaultValue) { + super(key, defaultValue); + } + + private SlimePropertyShort(String key, Short defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected ShortBinaryTag createTag(final Short value) { + return ShortBinaryTag.shortBinaryTag(value); + } + + @Override + protected Short readValue(final ShortBinaryTag tag) { + return tag.value(); + } + + @Override + protected ShortBinaryTag cast(BinaryTag rawTag) { + return (ShortBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyString.java b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyString.java new file mode 100644 index 000000000..fccfeae9d --- /dev/null +++ b/api/src/main/java/com/infernalsuite/asp/api/world/properties/type/SlimePropertyString.java @@ -0,0 +1,58 @@ +package com.infernalsuite.asp.api.world.properties.type; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.world.properties.SlimeProperty; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * A slime property of type integer + */ +public class SlimePropertyString extends SlimeProperty { + + public static SlimePropertyString create(final @NotNull String key, final String defaultValue) { + Preconditions.checkNotNull(key, "Key cannot be null"); + return new SlimePropertyString(key, defaultValue); + } + + public static SlimePropertyString create(final @NotNull String key, final String defaultValue, final @NotNull Function validator) { + Preconditions.checkNotNull(key, "Key cannot be null"); + Preconditions.checkNotNull(validator, "Use SlimePropertyString#create(String, String) instead"); + return new SlimePropertyString(key, defaultValue, validator); + } + + /** + * @deprecated Use {@link #create(String, String)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyString(String key, String defaultValue) { + super(key, defaultValue); + } + + /** + * @deprecated Use {@link #create(String, String, Function)} instead + */ + @Deprecated(forRemoval = true) + public SlimePropertyString(String key, String defaultValue, Function validator) { + super(key, defaultValue, validator); + } + + @Override + protected StringBinaryTag createTag(final String value) { + return StringBinaryTag.stringBinaryTag(value); + } + + @Override + protected String readValue(final StringBinaryTag tag) { + return tag.value(); + } + + @Override + protected StringBinaryTag cast(BinaryTag rawTag) { + return (StringBinaryTag) rawTag; + } + +} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostCreateEmptyWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostCreateEmptyWorldEvent.java deleted file mode 100644 index 654896236..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostCreateEmptyWorldEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPostCreateEmptyWorldEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final SlimeWorld slimeWorld; - - public AsyncPostCreateEmptyWorldEvent(SlimeWorld slimeWorld) { - super(true); - this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - public SlimeWorld getSlimeWorld() { - return slimeWorld; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostGetWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostGetWorldEvent.java deleted file mode 100644 index 0a0552062..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostGetWorldEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPostGetWorldEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final SlimeWorld slimeWorld; - - public AsyncPostGetWorldEvent(SlimeWorld slimeWorld) { - super(true); - this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - public SlimeWorld getSlimeWorld() { - return slimeWorld; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostImportWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostImportWorldEvent.java deleted file mode 100644 index 08fe02616..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostImportWorldEvent.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Objects; - -public class AsyncPostImportWorldEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final File worldDir; - private final String worldName; - private final SlimeLoader slimeLoader; - - public AsyncPostImportWorldEvent(File worldDir, String worldName, SlimeLoader slimeLoader) { - super(true); - this.worldDir = Objects.requireNonNull(worldDir, "worldDir cannot be null"); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - public File getWorldDir() { - return this.worldDir; - } - - public String getWorldName() { - return this.worldName; - } - - public SlimeLoader getSlimeLoader() { - return this.slimeLoader; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostLoadWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostLoadWorldEvent.java deleted file mode 100644 index 3c9374f63..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostLoadWorldEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPostLoadWorldEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final SlimeWorld slimeWorld; - - public AsyncPostLoadWorldEvent(SlimeWorld slimeWorld) { - super(true); - this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - public SlimeWorld getSlimeWorld() { - return this.slimeWorld; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostMigrateWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostMigrateWorldEvent.java deleted file mode 100644 index d9a8fa2b0..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPostMigrateWorldEvent.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPostMigrateWorldEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final String worldName; - private final SlimeLoader currentLoader; - private final SlimeLoader newLoader; - - public AsyncPostMigrateWorldEvent(String worldName, SlimeLoader currentLoader, SlimeLoader newLoader) { - super(true); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - this.currentLoader = Objects.requireNonNull(currentLoader, "currentLoader cannot be null"); - this.newLoader = Objects.requireNonNull(newLoader, "newLoader cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - public String getWorldName() { - return this.worldName; - } - - public SlimeLoader getCurrentLoader() { - return this.currentLoader; - } - - public SlimeLoader getNewLoader() { - return this.newLoader; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreCreateEmptyWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreCreateEmptyWorldEvent.java deleted file mode 100644 index 4bc35a5f2..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreCreateEmptyWorldEvent.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPreCreateEmptyWorldEvent extends Event implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean isCancelled; - private SlimeLoader slimeLoader; - private String worldName; - private boolean readOnly; - private SlimePropertyMap slimePropertyMap; - - public AsyncPreCreateEmptyWorldEvent(SlimeLoader slimeLoader, String worldName, boolean readOnly, SlimePropertyMap slimePropertyMap) { - super(true); - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - this.readOnly = readOnly; - this.slimePropertyMap = Objects.requireNonNull(slimePropertyMap, "slimePropertyMap cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } - - public SlimeLoader getSlimeLoader() { - return this.slimeLoader; - } - - public void setSlimeLoader(SlimeLoader slimeLoader) { - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - } - - public String getWorldName() { - return this.worldName; - } - - public void setWorldName(String worldName) { - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - } - - public boolean isReadOnly() { - return this.readOnly; - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public SlimePropertyMap getSlimePropertyMap() { - return this.slimePropertyMap; - } - - public void setSlimePropertyMap(SlimePropertyMap slimePropertyMap) { - this.slimePropertyMap = Objects.requireNonNull(slimePropertyMap, "slimePropertyMap cannot be null"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreGetWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreGetWorldEvent.java deleted file mode 100644 index a73796e92..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreGetWorldEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPreGetWorldEvent extends Event implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean isCancelled; - private String worldName; - - public AsyncPreGetWorldEvent(String worldName) { - super(true); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } - - public String getWorldName() { - return this.worldName; - } - - public void setWorldName(String worldName) { - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreImportWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreImportWorldEvent.java deleted file mode 100644 index a42df4b3a..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreImportWorldEvent.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Objects; - -public class AsyncPreImportWorldEvent extends Event implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean isCancelled; - private File worldDir; - private String worldName; - private SlimeLoader slimeLoader; - - public AsyncPreImportWorldEvent(File worldDir, String worldName, SlimeLoader slimeLoader) { - super(true); - this.worldDir = Objects.requireNonNull(worldDir, "worldDir cannot be null"); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } - - public File getWorldDir() { - return this.worldDir; - } - - public void setWorldDir(File worldDir) { - this.worldDir = Objects.requireNonNull(worldDir, "worldDir cannot be null"); - } - - public String getWorldName() { - return this.worldName; - } - - public void setWorldName(String worldName) { - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - } - - public SlimeLoader getSlimeLoader() { - return this.slimeLoader; - } - - public void setSlimeLoader(SlimeLoader slimeLoader) { - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreLoadWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreLoadWorldEvent.java deleted file mode 100644 index 716bc4315..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/AsyncPreLoadWorldEvent.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class AsyncPreLoadWorldEvent extends Event implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean isCancelled; - private SlimeLoader slimeLoader; - private String worldName; - private boolean readOnly; - private SlimePropertyMap slimePropertyMap; - - public AsyncPreLoadWorldEvent(SlimeLoader slimeLoader, String worldName, boolean readOnly, SlimePropertyMap slimePropertyMap) { - super(true); - this.slimeLoader = Objects.requireNonNull(slimeLoader, "slimeLoader cannot be null"); - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - this.readOnly = readOnly; - this.slimePropertyMap = Objects.requireNonNull(slimePropertyMap, "slimePropertyMap cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } - - public SlimeLoader getSlimeLoader() { - return this.slimeLoader; - } - - public void setSlimeLoader(SlimeLoader slimeLoader) { - this.slimeLoader = Objects.requireNonNull(slimeLoader, "Loader cannot be null"); - } - - public String getWorldName() { - return this.worldName; - } - - public void setWorldName(String worldName) { - this.worldName = Objects.requireNonNull(worldName, "worldName cannot be null"); - } - - public boolean isReadOnly() { - return this.readOnly; - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public SlimePropertyMap getSlimePropertyMap() { - return this.slimePropertyMap; - } - - public void setSlimePropertyMap(SlimePropertyMap slimePropertyMap) { - this.slimePropertyMap = Objects.requireNonNull(slimePropertyMap, "slimePropertyMap cannot be null"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/events/PreGenerateWorldEvent.java b/api/src/main/java/com/infernalsuite/aswm/api/events/PreGenerateWorldEvent.java deleted file mode 100644 index a0401d689..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/events/PreGenerateWorldEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.infernalsuite.aswm.api.events; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class PreGenerateWorldEvent extends Event implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean isCancelled; - private SlimeWorld slimeWorld; - - public PreGenerateWorldEvent(SlimeWorld slimeWorld) { - super(false); - this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); - } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } - - public SlimeWorld getSlimeWorld() { - return this.slimeWorld; - } - - public void setSlimeWorld(SlimeWorld slimeWorld) { - this.slimeWorld = Objects.requireNonNull(slimeWorld, "slimeWorld cannot be null"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidVersionException.java b/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidVersionException.java deleted file mode 100644 index eb25d0af6..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidVersionException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.infernalsuite.aswm.api.exceptions; - -/** - * Exception thrown when SWM is loaded - * on a non-supported Spigot version. - */ -public class InvalidVersionException extends SlimeException { - - public InvalidVersionException(String version) { - super("SlimeWorldManager does not support Spigot " + version + "!"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidWorldException.java b/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidWorldException.java deleted file mode 100644 index 0ad2d751d..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/exceptions/InvalidWorldException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.infernalsuite.aswm.api.exceptions; - -import java.io.File; - -/** - * Exception thrown when a folder does - * not contain a valid Minecraft world. - */ -public class InvalidWorldException extends SlimeException { - - public InvalidWorldException(File worldDir, String reason) { - super("Directory " + worldDir.getPath() + " does not contain a valid MC world! " + reason); - } - - public InvalidWorldException(File worldDir) { - super("Directory " + worldDir.getPath() + " does not contain a valid MC world!"); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorldInstance.java b/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorldInstance.java deleted file mode 100644 index d8fc5c2cd..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/SlimeWorldInstance.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.infernalsuite.aswm.api.world; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import org.bukkit.World; - -public interface SlimeWorldInstance { - - String getName(); - - World getBukkitWorld(); - - SlimeWorld getSlimeWorldMirror(); - - SlimePropertyMap getPropertyMap(); - - boolean isReadOnly(); - - SlimeLoader getLoader(); - - CompoundTag getExtraData(); - -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperties.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperties.java deleted file mode 100644 index fb4f35e65..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperties.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties; - -import com.infernalsuite.aswm.api.world.properties.type.SlimePropertyBoolean; -import com.infernalsuite.aswm.api.world.properties.type.SlimePropertyFloat; -import com.infernalsuite.aswm.api.world.properties.type.SlimePropertyInt; -import com.infernalsuite.aswm.api.world.properties.type.SlimePropertyString; -import org.jetbrains.annotations.ApiStatus; - -/** - * Class with all existing slime world properties. - */ -public class SlimeProperties { - - /** - * The X coordinate of the world spawn - */ - public static final SlimeProperty SPAWN_X = new SlimePropertyInt("spawnX", 0); - - /** - * The Y coordinate of the world spawn - */ - public static final SlimeProperty SPAWN_Y = new SlimePropertyInt("spawnY", 255); - - /** - * The Z coordinate of the world spawn - */ - public static final SlimeProperty SPAWN_Z = new SlimePropertyInt("spawnZ", 0); - - /** - * The yaw of the world spawn - */ - public static final SlimeProperty SPAWN_YAW = new SlimePropertyFloat("spawnYaw", 0.0f); - - /** - * The difficulty set for the world - */ - public static final SlimeProperty DIFFICULTY = new SlimePropertyString("difficulty", "peaceful", (value) -> - value.equalsIgnoreCase("peaceful") || value.equalsIgnoreCase("easy") - || value.equalsIgnoreCase("normal") || value.equalsIgnoreCase("hard") - ); - - /** - * Whether monsters are allowed to spawn at night or in the dark - */ - public static final SlimeProperty ALLOW_MONSTERS = new SlimePropertyBoolean("allowMonsters", true); - - /** - * Whether peaceful animals are allowed to spawn - */ - public static final SlimeProperty ALLOW_ANIMALS = new SlimePropertyBoolean("allowAnimals", true); - - /** - * Whether the dragon battle should be enabled in end worlds - */ - public static final SlimeProperty DRAGON_BATTLE = new SlimePropertyBoolean("dragonBattle", false); - - /** - * Whether PVP combat is allowed - */ - public static final SlimeProperty PVP = new SlimePropertyBoolean("pvp", true); - - /** - * The environment of the world - */ - public static final SlimeProperty ENVIRONMENT = new SlimePropertyString("environment", "normal", (value) -> - value.equalsIgnoreCase("normal") || value.equalsIgnoreCase("nether") || value.equalsIgnoreCase("the_end") - ); - - /** - * The type of world - */ - public static final SlimeProperty WORLD_TYPE = new SlimePropertyString("worldtype", "default", (value) -> - value.equalsIgnoreCase("default") || value.equalsIgnoreCase("flat") || value.equalsIgnoreCase("large_biomes") - || value.equalsIgnoreCase("amplified") || value.equalsIgnoreCase("customized") - || value.equalsIgnoreCase("debug_all_block_states") || value.equalsIgnoreCase("default_1_1") - ); - - /** - * The default biome generated in empty chunks - */ - public static final SlimeProperty DEFAULT_BIOME = new SlimePropertyString("defaultBiome", "minecraft:plains"); - - @ApiStatus.Experimental - public static final SlimeProperty SHOULD_LIMIT_SAVE = new SlimePropertyBoolean("hasSaveBounds", false); - - @ApiStatus.Experimental - public static final SlimeProperty SAVE_MIN_X = new SlimePropertyInt("saveMinX", 0); - @ApiStatus.Experimental - public static final SlimeProperty SAVE_MIN_Z = new SlimePropertyInt("saveMinZ", 0); - - @ApiStatus.Experimental - public static final SlimeProperty SAVE_MAX_X = new SlimePropertyInt("saveMaxX", 0); - @ApiStatus.Experimental - public static final SlimeProperty SAVE_MAX_Z = new SlimePropertyInt("saveMaxZ", 0); - - @ApiStatus.Experimental - public static final SlimeProperty CHUNK_PRUNING = new SlimePropertyString("pruning", "aggressive", (value) -> - value.equalsIgnoreCase("aggressive") || value.equalsIgnoreCase("never") - ); - - - - @ApiStatus.Experimental - public static final SlimeProperty CHUNK_SECTION_MIN = new SlimePropertyInt("chunkSectionMin", -4); - @ApiStatus.Experimental - public static final SlimeProperty CHUNK_SECTION_MAX = new SlimePropertyInt("chunkSectionMin", 19); - - -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperty.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperty.java deleted file mode 100644 index 9023fa1f8..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimeProperty.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.Tag; - -import java.util.function.Function; - -/** - * A property describing behavior of a slime world. - */ -public abstract class SlimeProperty { - - private final String nbtName; - private final T defaultValue; - private final Function validator; - - protected SlimeProperty(String nbtName, T defaultValue) { - this(nbtName, defaultValue, null); - } - - protected SlimeProperty(String nbtName, T defaultValue, Function validator) { - this.nbtName = nbtName; - - if (defaultValue != null && validator != null && !validator.apply(defaultValue)) { - throw new IllegalArgumentException("Invalid default value for property " + nbtName + "! " + defaultValue); - } - - this.defaultValue = defaultValue; - this.validator = validator; - } - - protected abstract void writeValue(CompoundMap compound, T value); - - protected abstract T readValue(Tag compoundTag); - - public String getNbtName() { - return nbtName; - } - - public T getDefaultValue() { - return defaultValue; - } - - public Function getValidator() { - return validator; - } - - @Override - public String toString() { - return "SlimeProperty{" + - "nbtName='" + nbtName + '\'' + - ", defaultValue=" + defaultValue + - '}'; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimePropertyMap.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimePropertyMap.java deleted file mode 100644 index a537a4f74..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/SlimePropertyMap.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; - -/** - * A Property Map object. - */ -public class SlimePropertyMap { - - private final CompoundMap properties; - - public SlimePropertyMap(CompoundMap compoundMap) { - this.properties = compoundMap; - } - - public SlimePropertyMap() { - this(new CompoundMap()); - } - - /** - * Return the current value of the given property - * - * @param property The slime property - * @return The current value - */ - public T getValue(SlimeProperty property) { - if (properties.containsKey(property.getNbtName())) { - return property.readValue(properties.get(property.getNbtName())); - } else { - return property.getDefaultValue(); - } - } - - /** - * Return the properties (CompoundMap) - * - * @return The properties - */ - public CompoundMap getProperties() { - return this.properties; - } - - /** - * Update the value of the given property - * - * @param property The slime property - * @param value The new value - * @throws IllegalArgumentException if the value fails validation. - */ - public void setValue(SlimeProperty property, T value) { - if (property.getValidator() != null && !property.getValidator().apply(value)) { - throw new IllegalArgumentException("'" + value + "' is not a valid property value."); - } - - property.writeValue(properties, value); - } - - /** - * Copies all values from the specified {@link SlimePropertyMap}. - * If the same property has different values on both maps, the one - * on the providen map will be used. - * - * @param propertyMap A {@link SlimePropertyMap}. - */ - public void merge(SlimePropertyMap propertyMap) { - properties.putAll(propertyMap.properties); - } - - /** - * Returns a {@link CompoundTag} containing every property set in this map. - * - * @return A {@link CompoundTag} with all the properties stored in this map. - */ - public CompoundTag toCompound() { - return new CompoundTag("properties", properties); - } - - public static SlimePropertyMap fromCompound(CompoundTag compound) { - return new SlimePropertyMap(compound.getValue()); - } - - public SlimePropertyMap clone() { - return new SlimePropertyMap(new CompoundMap(this.properties)); - } - - @Override - public String toString() { - return "SlimePropertyMap" + properties; - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyBoolean.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyBoolean.java deleted file mode 100644 index 6b43ec4e8..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyBoolean.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties.type; - -import com.infernalsuite.aswm.api.world.properties.SlimeProperty; -import com.flowpowered.nbt.ByteTag; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.Tag; - -import java.util.function.Function; - -/** - * A slime property of type boolean - */ -public class SlimePropertyBoolean extends SlimeProperty { - - public SlimePropertyBoolean(String nbtName, Boolean defaultValue) { - super(nbtName, defaultValue); - } - - public SlimePropertyBoolean(String nbtName, Boolean defaultValue, Function validator) { - super(nbtName, defaultValue, validator); - } - - @Override - protected void writeValue(CompoundMap compound, Boolean value) { - compound.put(getNbtName(), new ByteTag(getNbtName(), (byte) (value ? 1 : 0))); - } - - @Override - protected Boolean readValue(Tag compoundTag) { - return compoundTag.getAsByteTag() - .map((value) -> value.getValue() == 1) - .orElse(getDefaultValue()); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyFloat.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyFloat.java deleted file mode 100644 index 5c0bfc00c..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyFloat.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties.type; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.FloatTag; -import com.flowpowered.nbt.Tag; -import com.infernalsuite.aswm.api.world.properties.SlimeProperty; - -import java.util.function.Function; - -/** - * A slime property of type float - */ -public class SlimePropertyFloat extends SlimeProperty { - - public SlimePropertyFloat(String nbtName, Float defaultValue, Function validator) { - super(nbtName, defaultValue, validator); - } - - public SlimePropertyFloat(String nbtName, Float defaultValue) { - super(nbtName, defaultValue); - } - - @Override - protected void writeValue(CompoundMap compound, Float value) { - compound.put(getNbtName(), new FloatTag(getNbtName(), value)); - } - - @Override - protected Float readValue(Tag compoundTag) { - return compoundTag.getAsFloatTag() - .map(Tag::getValue) - .orElse(getDefaultValue()); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyInt.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyInt.java deleted file mode 100644 index 2a51125c6..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyInt.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties.type; - -import com.infernalsuite.aswm.api.world.properties.SlimeProperty; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.Tag; - -import java.util.function.Function; - -/** - * A slime property of type integer - */ -public class SlimePropertyInt extends SlimeProperty { - - public SlimePropertyInt(String nbtName, Integer defaultValue) { - super(nbtName, defaultValue); - } - - public SlimePropertyInt(String nbtName, Integer defaultValue, Function validator) { - super(nbtName, defaultValue, validator); - } - - @Override - protected void writeValue(CompoundMap compound, Integer value) { - compound.put(getNbtName(), new IntTag(getNbtName(), value)); - } - - @Override - protected Integer readValue(Tag compoundTag) { - return compoundTag.getAsIntTag() - .map(Tag::getValue) - .orElse(getDefaultValue()); - } -} diff --git a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyString.java b/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyString.java deleted file mode 100644 index 02eeb058f..000000000 --- a/api/src/main/java/com/infernalsuite/aswm/api/world/properties/type/SlimePropertyString.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.infernalsuite.aswm.api.world.properties.type; - -import com.infernalsuite.aswm.api.world.properties.SlimeProperty; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.StringTag; -import com.flowpowered.nbt.Tag; - -import java.util.function.Function; - -/** - * A slime property of type integer - */ -public class SlimePropertyString extends SlimeProperty { - - public SlimePropertyString(String nbtName, String defaultValue) { - super(nbtName, defaultValue); - } - - public SlimePropertyString(String nbtName, String defaultValue, Function validator) { - super(nbtName, defaultValue, validator); - } - - @Override - protected void writeValue(CompoundMap compound, String value) { - compound.put(getNbtName(), new StringTag(getNbtName(), value)); - } - - @Override - protected String readValue(Tag compoundTag) { - return compoundTag.getAsStringTag() - .map(Tag::getValue) - .orElse(getDefaultValue()); - } -} diff --git a/aspaper-api/build.gradle.kts.patch b/aspaper-api/build.gradle.kts.patch new file mode 100644 index 000000000..33a97d1b5 --- /dev/null +++ b/aspaper-api/build.gradle.kts.patch @@ -0,0 +1,62 @@ +--- a/paper-api/build.gradle.kts ++++ b/paper-api/build.gradle.kts +@@ -39,7 +_,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 @@ + 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() + idea { + module { + generatedSourceDirs.add(generatedApiPath.toFile()) +@@ -103,6 +_,18 @@ + main { + java { + srcDir(generatedApiPath) ++ srcDir(file("../paper-api/src/main/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/main/resources")) ++ } ++ } ++ test { ++ java { ++ srcDir(file("../paper-api/src/test/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/test/resources")) + } + } + } +@@ -169,7 +_,7 @@ + + tasks.withType { + val options = options as StandardJavadocDocletOptions +- options.overview = "src/main/javadoc/overview.html" ++ options.overview = "../paper-api/src/main/javadoc/overview.html" + options.use() + options.isDocFilesSubDirs = true + options.links( +@@ -202,11 +_,11 @@ + } + + // workaround for https://github.com/gradle/gradle/issues/4046 +- inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset") ++ inputs.dir("../paper-api/src/main/javadoc").withPropertyName("javadoc-sourceset") + val fsOps = services.fileSystemOperations + doLast { + fsOps.copy { +- from("src/main/javadoc") { ++ from("../paper-api/src/main/javadoc") { + include("**/doc-files/**") + } + into("build/docs/javadoc") diff --git a/aspaper-api/paper-patches/features/0001-API-Branding.patch b/aspaper-api/paper-patches/features/0001-API-Branding.patch new file mode 100644 index 000000000..6d40e9cfe --- /dev/null +++ b/aspaper-api/paper-patches/features/0001-API-Branding.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Sun, 9 Mar 2025 20:50:45 +0100 +Subject: [PATCH] API Branding + + +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java +index 652ff54e7c50412503725d628bfe72ed03059790..d26bbab5b67b6456a2713e67c2bcfa17e7478992 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfo.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java +@@ -19,6 +19,12 @@ public interface ServerBuildInfo { + */ + Key BRAND_PAPER_ID = Key.key("papermc", "paper"); + ++ ++ /** ++ * The brand id for AdvancedSlimePaper. ++ */ ++ Key BRAND_ADVANCED_SLIME_PAPER_ID = Key.key("infernalsuite", "advancedslimepaper"); ++ + /** + * Gets the {@code ServerBuildInfo}. + * diff --git a/aspaper-server/build.gradle.kts.patch b/aspaper-server/build.gradle.kts.patch new file mode 100644 index 000000000..35b337a9e --- /dev/null +++ b/aspaper-server/build.gradle.kts.patch @@ -0,0 +1,73 @@ +--- 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 + ++ val aspaper = forks.register("aspaper") { ++ upstream.patchDir("paperServer") { ++ upstreamPath = "paper-server" ++ excludes = setOf("src/minecraft", "patches", "build.gradle.kts") ++ patchesDir = rootDirectory.dir("aspaper-server/paper-patches") ++ outputDir = rootDirectory.dir("paper-server") ++ } ++ } ++ ++ activeFork = aspaper ++ + spigot { + buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42" + packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment +@@ -101,7 +_,20 @@ + } + } + +-val log4jPlugins = sourceSets.create("log4jPlugins") ++sourceSets { ++ main { ++ java { srcDir("../paper-server/src/main/java") } ++ resources { srcDir("../paper-server/src/main/resources") } ++ } ++ test { ++ java { srcDir("../paper-server/src/test/java") } ++ 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 @@ + } + + 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 @@ + val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", +- "Implementation-Title" to "Paper", ++ "Implementation-Title" to "AdvancedSlimePaper", //ASP + "Implementation-Version" to implementationVersion, + "Implementation-Vendor" to date, +- "Specification-Title" to "Paper", ++ "Specification-Title" to "AdvancedSlimePaper", //ASP + "Specification-Version" to project.version, +- "Specification-Vendor" to "Paper Team", +- "Brand-Id" to "papermc:paper", +- "Brand-Name" to "Paper", ++ "Specification-Vendor" to "InfernalSuite Team", //ASP ++ "Brand-Id" to "infernalsuite:advancedslimepaper", //ASP ++ "Brand-Name" to "AdvancedSlimePaper", //ASP + "Build-Number" to (build ?: ""), + "Build-Time" to buildTime.toString(), + "Git-Branch" to gitBranch, diff --git a/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch b/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch new file mode 100644 index 000000000..a48979525 --- /dev/null +++ b/aspaper-server/minecraft-patches/features/0001-Disable-dragon-battle.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: evlad +Date: Mon, 9 Sep 2024 21:33:11 +0300 +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 +--- 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 + ); + 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 +- this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit ++ // ASP START ++ if (bootstrap == null || bootstrap.initial().getPropertyMap().getValue(com.infernalsuite.asp.api.world.properties.SlimeProperties.DRAGON_BATTLE)) { ++ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit ++ } else { ++ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), new EndDragonFight.Data(false, true, true, false,Optional.empty(),Optional.empty(),Optional.empty())); // ASP - disable dragon ++ } ++ // ASP END + } else { + this.dragonFight = null; + } diff --git a/aspaper-server/minecraft-patches/features/0002-World-overrides.patch b/aspaper-server/minecraft-patches/features/0002-World-overrides.patch new file mode 100644 index 000000000..d7db4aa7f --- /dev/null +++ b/aspaper-server/minecraft-patches/features/0002-World-overrides.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> +Date: Mon, 26 Dec 2022 11:25:35 -0500 +Subject: [PATCH] World overrides + + +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 1509c96ae9247d1d5ddd0fc50210f88da10bf3de..bbc4be3c3811ee19bd791cb6626474038d35a298 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -516,18 +516,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Mon, 10 Mar 2025 11:41:14 +0100 +Subject: [PATCH] Avoid IO call for UUID + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 8429a97047f4321df96fd11559867bce74d0a49c..60608cea4e564c1ce47f8a4de2c1a48986bbdecb 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -617,7 +617,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor + this.pvpMode = server.isPvpAllowed(); + 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 + // 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 new file mode 100644 index 000000000..8d003194a --- /dev/null +++ b/aspaper-server/minecraft-patches/features/0004-Prevent-config-disk-io-on-world-load.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Wed, 12 Mar 2025 21:14:56 +0100 +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 +--- 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 + ) { + //ASP end + // CraftBukkit start +- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor ++ 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 +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index 1dbe7c7c1051c3972105534a07ce50d4cf98fc85..e1d3c292b9efccb032245f4f1618f2650f0bc619 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 + 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); 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 new file mode 100644 index 000000000..bd3e2ff80 --- /dev/null +++ b/aspaper-server/minecraft-patches/features/0005-Read-only-dimension-data-store.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Thu, 13 Mar 2025 00:09:20 +0100 +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 +--- 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 + LOGGER.error("Failed to create dimension data storage directory", (Throwable)var15); + } + +- this.dataStorage = new DimensionDataStorage(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()); ++ } else { ++ this.dataStorage = new DimensionDataStorage(path, fixerUpper, level.registryAccess()); ++ } ++ //ASP end - No dimension data storage + this.chunkMap = new ChunkMap( + level, + levelStorageAccess, 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 new file mode 100644 index 000000000..cfd198c02 --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java.patch @@ -0,0 +1,13 @@ +--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +@@ -184,7 +_,9 @@ + }; + } + +- public void close(final boolean save, final boolean halt) { ++ public void close(boolean save, boolean halt) { // ASP ++ if (this.world instanceof com.infernalsuite.asp.level.SlimeLevelInstance) save = false; // ASP ++ + TickThread.ensureTickThread("Closing world off-main"); + if (halt) { + LOGGER.info("Waiting 60s for chunk system to halt for world '" + WorldUtil.getWorldName(this.world) + "'"); 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 new file mode 100644 index 000000000..ff3cc89a5 --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java.patch @@ -0,0 +1,46 @@ +--- 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 @@ + } + + 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); ++ } ++ //ASP end + this.world.unload(levelChunk); + } ++ + } + + // unload entity data + if (entityChunk != null) { +- this.saveEntities(entityChunk, true); ++ //ASP start - prevent saving ++ if(!(world instanceof com.infernalsuite.asp.level.SlimeLevelInstance)) { ++ this.saveEntities(entityChunk, true); ++ } ++ //ASP end - prevent saving + // yes this is a hack to pass the compound tag through... + final CompoundTag lastEntityUnload = this.lastEntityUnload; + this.lastEntityUnload = null; diff --git a/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java.patch b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java.patch new file mode 100644 index 000000000..03353e87b --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java.patch @@ -0,0 +1,46 @@ +--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java +@@ -33,7 +_,7 @@ + private static final Logger LOGGER = LoggerFactory.getLogger(ChunkLoadTask.class); + + public final NewChunkHolder chunkHolder; +- private final ChunkDataLoadTask loadTask; ++ private final com.infernalsuite.asp.level.CommonLoadTask loadTask; // ASP + + private volatile boolean cancelled; + private NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask; +@@ -45,11 +_,20 @@ + final NewChunkHolder chunkHolder, final Priority priority) { + super(scheduler, world, chunkX, chunkZ); + this.chunkHolder = chunkHolder; +- this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); +- this.loadTask.addCallback((final GenericDataLoadTask.TaskResult result) -> { +- ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement +- ChunkLoadTask.this.tryCompleteLoad(); +- }); ++ // ASWM start ++ if (world instanceof com.infernalsuite.asp.level.SlimeLevelInstance levelInstance) { ++ this.loadTask = levelInstance.getLoadTask(this, scheduler, world, chunkX, chunkZ, priority, result -> { ++ ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement ++ ChunkLoadTask.this.tryCompleteLoad(); ++ }); ++ } else { ++ this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); ++ ((ChunkDataLoadTask) this.loadTask).addCallback((final GenericDataLoadTask.TaskResult result) -> { ++ ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement ++ ChunkLoadTask.this.tryCompleteLoad(); ++ }); ++ } ++ // ASWM end + } + + private void tryCompleteLoad() { +@@ -276,7 +_,7 @@ + + private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {} + +- private static final class ChunkDataLoadTask extends CallbackDataLoadTask { ++ private static final class ChunkDataLoadTask extends CallbackDataLoadTask implements com.infernalsuite.asp.level.CommonLoadTask { // ASP + private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, + final int chunkZ, final Priority priority) { + super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority); 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 new file mode 100644 index 000000000..44ce62a20 --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -0,0 +1,71 @@ +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -570,7 +_,32 @@ + } + // Paper end - chunk tick iteration + +- public ServerLevel( ++ public com.infernalsuite.asp.level.SlimeInMemoryWorld slimeInstance; // ASP ++ ++ public ServerLevel( ++ MinecraftServer server, ++ Executor dispatcher, ++ LevelStorageSource.LevelStorageAccess levelStorageAccess, ++ net.minecraft.world.level.storage.PrimaryLevelData serverLevelData, // CraftBukkit ++ ResourceKey dimension, ++ LevelStem levelStem, ++ ChunkProgressListener progressListener, ++ boolean isDebug, ++ long biomeZoomSeed, ++ List customSpawners, ++ boolean tickTime, ++ @Nullable RandomSequences randomSequences, ++ org.bukkit.World.Environment env, // CraftBukkit ++ org.bukkit.generator.ChunkGenerator gen, // CraftBukkit ++ org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit ++ ) { ++ //ASP start ++ this(null, server, dispatcher, levelStorageAccess, serverLevelData, dimension, levelStem, progressListener, ++ isDebug, biomeZoomSeed, customSpawners, tickTime, randomSequences, env, gen, biomeProvider); ++ } ++ ++ public ServerLevel( ++ @Nullable com.infernalsuite.asp.level.SlimeBootstrap bootstrap, + MinecraftServer server, + Executor dispatcher, + LevelStorageSource.LevelStorageAccess levelStorageAccess, +@@ -587,6 +_,7 @@ + org.bukkit.generator.ChunkGenerator gen, // CraftBukkit + org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit + ) { ++ //ASP end + // CraftBukkit start + super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor + this.pvpMode = server.isPvpAllowed(); +@@ -614,6 +_,13 @@ + chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); + } + // CraftBukkit end ++ // ASP START ++ ChunkGenerator result = this.getGenerator(bootstrap); ++ if (result != null) { ++ chunkGenerator = result; ++ } ++ // ASP END ++ + boolean flag = server.forceSynchronousWrites(); + DataFixer fixerUpper = server.getFixerUpper(); + // Paper - rewrite chunk system +@@ -695,6 +_,12 @@ + public void setDragonFight(@Nullable EndDragonFight dragonFight) { + this.dragonFight = dragonFight; + } ++ ++ // ASP START ++ public ChunkGenerator getGenerator(com.infernalsuite.asp.level.SlimeBootstrap bootstrap) { ++ return null; ++ } ++ // ASP END + + public void setWeatherParameters(int clearTime, int weatherTime, boolean isRaining, boolean isThundering) { + this.serverLevelData.setClearWeatherTime(clearTime); 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 new file mode 100644 index 000000000..81744efd8 --- /dev/null +++ b/aspaper-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch @@ -0,0 +1,15 @@ +--- 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/0001-Warning-if-people-use-old-swm-api.patch b/aspaper-server/paper-patches/features/0001-Warning-if-people-use-old-swm-api.patch new file mode 100644 index 000000000..0f685c0ec --- /dev/null +++ b/aspaper-server/paper-patches/features/0001-Warning-if-people-use-old-swm-api.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: kyngs +Date: Sat, 1 Jun 2024 18:57:39 +0200 +Subject: [PATCH] Warning if people use old swm api + + +diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java +index 3e82ea07ca4194844c5528446e2c4a46ff4acee5..1f8bff31ce60f9a1b143e749916fa51cf115f5d7 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java +@@ -64,6 +64,15 @@ class PaperPluginInstanceManager { + } + + public @Nullable Plugin getPlugin(@NotNull String name) { ++ // ASP start - Warn if someone tries to get the old API instance ++ if (name.equals("SlimeWorldManager")) { ++ server.getLogger().warning(""" ++ Hey! It seems like you're trying to access the old SlimeWorldManager API. ++ Since 1.21.0 the API is now provided by the server directly. ++ See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. ++ """); ++ } ++ // ASP end + return this.lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Paper + } + diff --git a/aspaper-server/paper-patches/features/0002-Warning-if-people-use-old-swm-plugin.patch b/aspaper-server/paper-patches/features/0002-Warning-if-people-use-old-swm-plugin.patch new file mode 100644 index 000000000..85c1ae904 --- /dev/null +++ b/aspaper-server/paper-patches/features/0002-Warning-if-people-use-old-swm-plugin.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: kyngs +Date: Sat, 1 Jun 2024 18:57:39 +0200 +Subject: [PATCH] Warning if people use old swm plugin + + +diff --git a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java +index 26422904751647a061397ce978bba752149003cd..4940083475948eac4fc06446f7ee7e1e8e04d676 100644 +--- a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java ++++ b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java +@@ -26,6 +26,15 @@ public abstract class SimpleProviderStorage implements ProviderStorage { + + @Override + public void register(PluginProvider provider) { ++ // ASP start - sanity check for old SlimeWorldManager ++ if (provider.getMeta().getName().equals("SlimeWorldManager")) { ++ LOGGER.warn(""" ++ Hey! It looks like you're trying to load the old SlimeWorldManager plugin. ++ ASP no longer works like that, and you should remove the plugin from your server. ++ See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. ++ """); ++ return; ++ } // ASP end + this.providers.add(provider); + } + diff --git a/aspaper-server/paper-patches/features/0003-Branding.patch b/aspaper-server/paper-patches/features/0003-Branding.patch new file mode 100644 index 000000000..bddf7be57 --- /dev/null +++ b/aspaper-server/paper-patches/features/0003-Branding.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Sun, 9 Mar 2025 20:58:19 +0100 +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 +--- 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 { + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final int DISTANCE_ERROR = -1; + private static final int DISTANCE_UNKNOWN = -2; +- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper"; ++ private static final String DOWNLOAD_PAGE = "https://discord.gg/YevvsMa"; + + @Override + public long getCacheTime() { +@@ -49,7 +49,7 @@ public class PaperVersionFetcher implements VersionFetcher { + if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) { + updateMessage = text("You are running a development version without access to version information", color(0xFF5300)); + } else { +- updateMessage = getUpdateStatusMessage("PaperMC/Paper", build); ++ updateMessage = getUpdateStatusMessage("InfernalSuite/AdvancedSlimePaper", build); + } + final @Nullable Component history = this.getHistory(); + +@@ -59,16 +59,16 @@ public class PaperVersionFetcher implements VersionFetcher { + private static Component getUpdateStatusMessage(final String repo, final ServerBuildInfo build) { + int distance = DISTANCE_ERROR; + +- final OptionalInt buildNumber = build.buildNumber(); ++ /*final OptionalInt buildNumber = build.buildNumber(); //ASP start + if (buildNumber.isPresent()) { + distance = fetchDistanceFromSiteApi(build, buildNumber.getAsInt()); +- } else { ++ } else { */ //ASP End + final Optional gitBranch = build.gitBranch(); + final Optional gitCommit = build.gitCommit(); + if (gitBranch.isPresent() && gitCommit.isPresent()) { + distance = fetchDistanceFromGitHub(repo, gitBranch.get(), gitCommit.get()); + } +- } ++ //} //ASP + + return switch (distance) { + case DISTANCE_ERROR -> text("Error obtaining version information", NamedTextColor.YELLOW); +@@ -76,7 +76,7 @@ public class PaperVersionFetcher implements VersionFetcher { + case DISTANCE_UNKNOWN -> text("Unknown version", NamedTextColor.YELLOW); + default -> text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW) + .append(Component.newline()) +- .append(text("Download the new version at: ") ++ .append(text("Download the new version from our Discord: ") + .append(text(DOWNLOAD_PAGE, NamedTextColor.GOLD) + .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 +--- 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( + private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit"; + + private static final String BRAND_PAPER_NAME = "Paper"; ++ private static final String BRAND_ADVANCED_SLIME_PAPER_NAME = "AdvancedSlimePaper"; + + private static final String BUILD_DEV = "DEV"; + +@@ -42,9 +43,9 @@ public record ServerBuildInfoImpl( + this( + getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) + .map(Key::key) +- .orElse(BRAND_PAPER_ID), ++ .orElse(BRAND_ADVANCED_SLIME_PAPER_ID), + getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) +- .orElse(BRAND_PAPER_NAME), ++ .orElse(BRAND_ADVANCED_SLIME_PAPER_NAME), + SharedConstants.getCurrentVersion().getId(), + SharedConstants.getCurrentVersion().getName(), + getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) +@@ -61,7 +62,7 @@ public record ServerBuildInfoImpl( + + @Override + public boolean isBrandCompatible(final @NotNull Key brandId) { +- return brandId.equals(this.brandId); ++ return brandId.equals(this.brandId) || brandId.equals(BRAND_PAPER_ID) || brandId.equals(BRAND_ADVANCED_SLIME_PAPER_ID); + } + + @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 +--- 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 { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + +- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); ++ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/com.infernalsuite.asp/aspaper-api/pom.properties"); + Properties properties = new Properties(); + + if (stream != null) { 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 new file mode 100644 index 000000000..ff237d481 --- /dev/null +++ b/aspaper-server/paper-patches/features/0004-Delete-temp-folder-after-world-is-unloaded.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Mon, 10 Mar 2025 12:09:41 +0100 +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 +--- 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 { + handle.getChunkSource().close(save); + io.papermc.paper.FeatureHooks.closeEntityManager(handle, save); // SPIGOT-6722: close entityManager // Paper - chunk system + handle.levelStorageAccess.close(); ++ ++ //ASP start - avoid temp storage leak during runtime ++ if(handle instanceof com.infernalsuite.asp.level.SlimeLevelInstance asp) { ++ asp.deleteTempFiles(); ++ } ++ //ASP end + } catch (Exception ex) { + this.getLogger().log(Level.SEVERE, null, ex); + } 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 new file mode 100644 index 000000000..70c5ff9d8 --- /dev/null +++ b/aspaper-server/paper-patches/features/0005-Prevent-config-disk-io-on-world-load.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Wed, 12 Mar 2025 21:14:56 +0100 +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 +--- a/src/main/java/org/spigotmc/SpigotConfig.java ++++ b/src/main/java/org/spigotmc/SpigotConfig.java +@@ -78,7 +78,12 @@ public class SpigotConfig { + } + } + ++ // ASP start - Improve Slime IO + public static void readConfig(Class clazz, Object instance) { // Paper - package-private -> public ++ readConfig(clazz, instance, true); ++ } ++ public static void readConfig(Class clazz, Object instance, boolean save) { ++ // ASP end - Improve Slime IO + 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 { + } + + try { +- SpigotConfig.config.save(SpigotConfig.CONFIG_FILE); ++ // ASP start - Improve Slime IO ++ if(save) { ++ SpigotConfig.config.save(SpigotConfig.CONFIG_FILE); ++ } ++ // ASP end - Improve Slime IO + } catch (IOException ex) { + 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 +--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java ++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java +@@ -10,17 +10,28 @@ public class SpigotWorldConfig { + private final YamlConfiguration config; + private boolean verbose; + ++ // ASP start - Improve Slime IO + public SpigotWorldConfig(String worldName) { ++ this(worldName, true); ++ } ++ // ASP end - Improve Slime IO ++ ++ public SpigotWorldConfig(String worldName, boolean saveOnLoad) { // ASP - Improve Slime IO + this.worldName = worldName; + this.config = SpigotConfig.config; +- this.init(); ++ this.init(saveOnLoad); // ASP - Improve Slime IO + } + ++ // ASP start - Improve Slime IO + public void init() { ++ init(true); ++ } ++ public void init(boolean saveOnLoad) { ++ // ASP end - Improve Slime IO + this.verbose = this.getBoolean("verbose", false); // Paper + + this.log("-------- World Settings For [" + this.worldName + "] --------"); +- SpigotConfig.readConfig(SpigotWorldConfig.class, this); ++ SpigotConfig.readConfig(SpigotWorldConfig.class, this, saveOnLoad); // ASP - Improve Slime IO + } + + private void log(String s) { 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 new file mode 100644 index 000000000..94ef404f6 --- /dev/null +++ b/aspaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1508,6 +_,8 @@ + return false; + } + ++ com.infernalsuite.asp.AdvancedSlimePaper.instance().onWorldUnload(world.getName()); // ASP - Remove unloaded world from map ++ + try { + if (save) { + handle.save(null, true, false); // Paper - Fix saving in unloadWorld diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java b/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java new file mode 100644 index 000000000..3fb8c8ca6 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/AdvancedSlimePaper.java @@ -0,0 +1,250 @@ +package com.infernalsuite.asp; + +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.events.LoadSlimeWorldEvent; +import com.infernalsuite.asp.api.exceptions.*; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.loaders.SlimeSerializationAdapter; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.level.SlimeLevelInstance; +import com.infernalsuite.asp.serialization.SlimeSerializationAdapterImpl; +import com.infernalsuite.asp.serialization.anvil.AnvilImportData; +import com.infernalsuite.asp.serialization.anvil.AnvilWorldReader; +import com.infernalsuite.asp.serialization.slime.SlimeSerializer; +import com.infernalsuite.asp.serialization.slime.reader.SlimeWorldReaderRegistry; +import com.infernalsuite.asp.skeleton.SkeletonSlimeWorld; +import com.infernalsuite.asp.util.NmsUtil; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.server.level.ServerLevel; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.event.world.WorldLoadEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spigotmc.AsyncCatcher; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class AdvancedSlimePaper implements AdvancedSlimePaperAPI { + + private static final Logger LOGGER = LoggerFactory.getLogger(AdvancedSlimePaper.class); + private static final SlimeNMSBridge BRIDGE_INSTANCE = SlimeNMSBridge.instance(); + + private final Map loadedWorlds = new ConcurrentHashMap<>(); + + static { + System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); + } + + private final SlimeSerializationAdapter serializationAdapter = new SlimeSerializationAdapterImpl(); + + public static AdvancedSlimePaper instance() { + return (AdvancedSlimePaper) AdvancedSlimePaperAPI.instance(); + } + + @Override + public SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, SlimePropertyMap propertyMap) throws UnknownWorldException, IOException, CorruptedWorldException, NewerFormatException { + Objects.requireNonNull(loader, "Loader cannot be null"); + Objects.requireNonNull(worldName, "World name cannot be null"); + Objects.requireNonNull(propertyMap, "Properties cannot be null"); + + long start = System.currentTimeMillis(); + + LOGGER.info("Reading world {}.", worldName); + byte[] serializedWorld = loader.readWorld(worldName); + + SlimeWorld slimeWorld = SlimeWorldReaderRegistry.readWorld(loader, worldName, serializedWorld, propertyMap, readOnly); + LOGGER.info("Applying datafixers for {}.", worldName); + SlimeWorld dataFixed = SlimeNMSBridge.instance().getSlimeDataConverter().applyDataFixers(slimeWorld); + + // If the dataFixed and slimeWorld are same, then no datafixers were applied + if (!readOnly && dataFixed != slimeWorld) + loader.saveWorld(worldName, SlimeSerializer.serialize(dataFixed)); // Write dataFixed world back to loader + + LOGGER.info("World {} read in {}ms.", worldName, System.currentTimeMillis() - start); + + return dataFixed; + } + + @Override + public SlimeWorldInstance loadWorld(SlimeWorld world, boolean callWorldLoadEvent) throws IllegalArgumentException { + AsyncCatcher.catchOp("SWM world load"); + Objects.requireNonNull(world, "SlimeWorld cannot be null"); + + if (Bukkit.getWorld(world.getName()) != null) { + throw new IllegalArgumentException("World " + world.getName() + " is already loaded"); + } + + LOGGER.info("Loading world {}...", world.getName()); + long start = System.currentTimeMillis(); + + SlimeWorldInstance instance = BRIDGE_INSTANCE.loadInstance(world); + + Bukkit.getPluginManager().callEvent(new LoadSlimeWorldEvent(instance)); + if (callWorldLoadEvent) { + Bukkit.getPluginManager().callEvent(new WorldLoadEvent(instance.getBukkitWorld())); + } + + registerWorld(instance); + + LOGGER.info("World {} loaded in {}ms.", world.getName(), System.currentTimeMillis() - start); + return instance; + } + + @Override + public boolean worldLoaded(SlimeWorld world) { + return loadedWorlds.containsKey(world.getName()); + } + + @Override + public void saveWorld(SlimeWorld world) throws IOException { + Objects.requireNonNull(world, "SlimeWorld cannot be null"); + if (worldLoaded(world)) { + Future[] future = new Future[1]; + + // This is not pretty, but we really need to hop onto the main thread + NmsUtil.runSyncAndWait(() -> { + World bukkitWorld = Bukkit.getWorld(world.getName()); + + ServerLevel level = ((CraftWorld) bukkitWorld).getHandle(); + if (level instanceof SlimeLevelInstance slimeLevel) { + future[0] = slimeLevel.save(); + } else { + // Shouldn't happen + LOGGER.warn("ServerLevel based off of SlimeWorld is not an instance of SlimeLevelInstance. Falling back to default save method."); + bukkitWorld.save(); + } + }); + + if (future[0] != null) { + try { + future[0].get(); + } catch (InterruptedException exception) { + throw new RuntimeException(exception); + } catch (ExecutionException e) { + if (e.getCause() instanceof IOException ioException) { + throw ioException; + } else { + throw new RuntimeException(e.getCause()); + } + } + } + } else { + LOGGER.info("Saving unloaded world {}...", world.getName()); + Objects.requireNonNull(world.getLoader(), "World loader cannot be null"); + long start = System.currentTimeMillis(); + + byte[] serializedWorld = SlimeSerializer.serialize(world); + + long saveStart = System.currentTimeMillis(); + world.getLoader().saveWorld(world.getName(), serializedWorld); + + LOGGER.info("World {} serialized in {}ms and saved in {}ms.", world.getName(), saveStart - start, System.currentTimeMillis() - saveStart); + } + + } + + @Override + public SlimeWorldInstance getLoadedWorld(String worldName) { + return loadedWorlds.get(worldName); + } + + @Override + public List getLoadedWorlds() { + return List.copyOf(loadedWorlds.values()); + } + + @Override + public SlimeWorld createEmptyWorld(String worldName, boolean readOnly, SlimePropertyMap propertyMap, SlimeLoader loader) { + Objects.requireNonNull(worldName, "World name cannot be null"); + Objects.requireNonNull(propertyMap, "Properties cannot be null"); + + return new SkeletonSlimeWorld(worldName, loader, readOnly, new Long2ObjectOpenHashMap<>(0), new ConcurrentHashMap<>(), propertyMap, BRIDGE_INSTANCE.getCurrentVersion()); + } + + @Override + public void migrateWorld(String worldName, SlimeLoader currentLoader, SlimeLoader newLoader) throws IOException, WorldAlreadyExistsException, UnknownWorldException { + Objects.requireNonNull(worldName, "World name cannot be null"); + Objects.requireNonNull(currentLoader, "Current loader cannot be null"); + Objects.requireNonNull(newLoader, "New loader cannot be null"); + + if (newLoader.worldExists(worldName)) { + throw new WorldAlreadyExistsException(worldName); + } + + byte[] serializedWorld = currentLoader.readWorld(worldName); + newLoader.saveWorld(worldName, serializedWorld); + currentLoader.deleteWorld(worldName); + } + + @Override + public SlimeWorld readVanillaWorld(File worldDir, String worldName, SlimeLoader loader) throws InvalidWorldException, WorldLoadedException, WorldTooBigException, IOException, WorldAlreadyExistsException { + Objects.requireNonNull(worldDir, "World directory cannot be null"); + Objects.requireNonNull(worldName, "World name cannot be null"); + + if (loader != null && loader.worldExists(worldName)) { + throw new WorldAlreadyExistsException(worldName); + } + + World bukkitWorld = Bukkit.getWorld(worldName); + + if (bukkitWorld != null && BRIDGE_INSTANCE.getInstance(bukkitWorld) == null) { + throw new WorldLoadedException(worldDir.getName()); + } + + SlimeWorld world; + + try { + world = AnvilWorldReader.INSTANCE.readFromData(AnvilImportData.legacy(worldDir, worldName, loader)); + } catch (RuntimeException e) { + if (e.getCause() == null) { + throw e; + } + if (e.getCause() instanceof IOException ioException) { + throw ioException; + } else if (e.getCause() instanceof InvalidWorldException invalidWorldException) { + throw invalidWorldException; + } else { + throw e; + } + } + + // A sanity check to make sure the world is not too big to be serialized + try { + SlimeSerializer.serialize(world); + } catch (IndexOutOfBoundsException ex) { + throw new WorldTooBigException(worldDir.getName()); + } + + return world; + } + + @Override + public SlimeSerializationAdapter getSerializer() { + return this.serializationAdapter; + } + + /** + * Utility method to register a loaded {@link SlimeWorld} with the internal map (for {@link #getLoadedWorld} calls) + * + * @param world the world to register + */ + private void registerWorld(SlimeWorldInstance world) { + this.loadedWorlds.put(world.getName(), world); + } + + public void onWorldUnload(String name) { + this.loadedWorlds.remove(name); + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java new file mode 100644 index 000000000..848a6e1ce --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/Converter.java @@ -0,0 +1,131 @@ +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.minecraft.nbt.ByteArrayTag; +import net.minecraft.nbt.ByteTag; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.EndTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.IntArrayTag; +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.LongArrayTag; +import net.minecraft.nbt.LongTag; +import net.minecraft.nbt.ShortTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.level.chunk.DataLayer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class Converter { + + private static final Logger LOGGER = LogManager.getLogger("ASP NBT Converter"); + + static DataLayer convertArray(NibbleArray array) { + return new DataLayer(array.getBacking()); + } + + public static NibbleArray convertArray(DataLayer array) { + if(array == null) { + return null; + } + + return new NibbleArray(array.getData()); + } + + public static Tag convertTag(T tag) { + try { + return switch (tag.type().id()) { + case Tag.TAG_END -> EndTag.INSTANCE; + case Tag.TAG_BYTE -> ByteTag.valueOf(((ByteBinaryTag) tag).value()); + case Tag.TAG_SHORT -> ShortTag.valueOf(((ShortBinaryTag) tag).value()); + case Tag.TAG_INT -> IntTag.valueOf(((IntBinaryTag) tag).value()); + case Tag.TAG_LONG -> LongTag.valueOf(((LongBinaryTag) tag).value()); + case Tag.TAG_FLOAT -> FloatTag.valueOf(((FloatBinaryTag) tag).value()); + case Tag.TAG_DOUBLE -> DoubleTag.valueOf(((DoubleBinaryTag) tag).value()); + case Tag.TAG_BYTE_ARRAY -> new ByteArrayTag(((ByteArrayBinaryTag) tag).value()); + case Tag.TAG_STRING -> StringTag.valueOf(((StringBinaryTag) tag).value()); + case Tag.TAG_LIST -> { + ListTag list = new ListTag(); + for (BinaryTag entry : ((ListBinaryTag) tag)) list.add(convertTag(entry)); + yield list; + } + case Tag.TAG_COMPOUND -> { + CompoundTag compound = new CompoundTag(); + ((CompoundBinaryTag) tag).forEach(entry -> compound.put(entry.getKey(), convertTag(entry.getValue()))); + yield compound; + } + case Tag.TAG_INT_ARRAY -> new IntArrayTag(((IntArrayBinaryTag) tag).value()); + case Tag.TAG_LONG_ARRAY -> new LongArrayTag(((LongArrayBinaryTag) tag).value()); + default -> throw new IllegalArgumentException("Invalid tag type " + tag.type().id()); + }; + } catch (final Exception e) { + CompoundBinaryTag exceptionTag = CompoundBinaryTag.builder().put("failing_tag", tag).build(); + String tagString; + try { + tagString = TagStringIO.get().asString(exceptionTag); + } catch (final IOException ioEx) { + LOGGER.error("Error while trying to convert exception tag to string", ioEx); + tagString = "UNAVAILABLE"; + } + LOGGER.error("Failed to convert NBT object: {}", tagString); + throw e; + } + } + + @SuppressWarnings("unchecked") + 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_ARRAY -> (T) ByteArrayBinaryTag.byteArrayBinaryTag(((ByteArrayTag) base).getAsByteArray()); + case Tag.TAG_STRING -> (T) StringBinaryTag.stringBinaryTag(((StringTag) base).getAsString()); + 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); + } + 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)))); + yield (T) builder.build(); + } + case Tag.TAG_INT_ARRAY -> (T) IntArrayBinaryTag.intArrayBinaryTag(((IntArrayTag) base).getAsIntArray()); + case Tag.TAG_LONG_ARRAY -> (T) LongArrayBinaryTag.longArrayBinaryTag(((LongArrayTag) base).getAsLongArray()); + default -> throw new IllegalArgumentException("Invalid tag type " + base.getId()); + }; + } + +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/InternalPlugin.java b/aspaper-server/src/main/java/com/infernalsuite/asp/InternalPlugin.java new file mode 100644 index 000000000..f8a3a68f4 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/InternalPlugin.java @@ -0,0 +1,159 @@ +package com.infernalsuite.asp; + +import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.PluginLogger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.LogRecord; +import java.io.File; +import java.io.InputStream; +import java.util.List; + +public class InternalPlugin extends PluginBase { + private boolean enabled = true; + + private final String pluginName; + private PluginDescriptionFile pdf; + + public InternalPlugin() { + this.pluginName = "Minecraft"; + pdf = new PluginDescriptionFile(pluginName, "1.0", "nms"); + } + + @Override + public @NotNull Server getServer() { + return MinecraftServer.getServer().server; + } + + @Override + public @NotNull PluginLogger getLogger() { + return new PluginLogger(new InternalPlugin()) { + @Override + public void log(@NotNull LogRecord logRecord) { + MinecraftServer.LOGGER.info(logRecord.getMessage()); + } + }; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public File getDataFolder() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public PluginDescriptionFile getDescription() { + return pdf; + } + // Paper start + @Override + public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() { + return pdf; + } + // Paper end + + @Override + public FileConfiguration getConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public InputStream getResource(String filename) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveDefaultConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveResource(String resourcePath, boolean replace) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void reloadConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public PluginLoader getPluginLoader() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void onDisable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void onLoad() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void onEnable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isNaggable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void setNaggable(boolean canNag) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public LifecycleEventManager getLifecycleManager() { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java b/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java new file mode 100644 index 000000000..d1b255e55 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/SimpleDataFixerConverter.java @@ -0,0 +1,185 @@ +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.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.serialization.SlimeWorldReader; +import com.infernalsuite.asp.skeleton.SkeletonSlimeWorld; +import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; +import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import com.infernalsuite.asp.api.world.SlimeWorld; +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.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; + +class SimpleDataFixerConverter implements SlimeWorldReader, SlimeDataConverter { + + @Override + public SlimeWorld readFromData(SlimeWorld data) { + int newVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + int currentVersion = data.getDataVersion(); + // Already fixed + if (currentVersion == newVersion) { + return data; + } + + long encodedNewVersion = DataConverter.encodeVersions(newVersion, Integer.MAX_VALUE); + long encodedCurrentVersion = DataConverter.encodeVersions(currentVersion, Integer.MAX_VALUE); + + Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); + for (SlimeChunk chunk : data.getChunkStorage()) { + List entities = new ArrayList<>(); + List blockEntities = new ArrayList<>(); + for (CompoundBinaryTag upgradeEntity : chunk.getTileEntities()) { + blockEntities.add( + convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.TILE_ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) + ); + } + for (CompoundBinaryTag upgradeEntity : chunk.getEntities()) { + entities.add( + convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) + ); + } + long chunkPos = Util.chunkPosition(chunk.getX(), chunk.getZ()); + + SlimeChunkSection[] sections = new SlimeChunkSection[chunk.getSections().length]; + for (int i = 0; i < sections.length; i++) { + SlimeChunkSection dataSection = chunk.getSections()[i]; + if (dataSection == null) continue; + + CompoundBinaryTag blockStateTag = convertAndBack(dataSection.getBlockStatesTag(), (tag) -> { + WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, new NBTMapType(tag), "palette", encodedCurrentVersion, encodedNewVersion); + }); + + CompoundBinaryTag biomeTag = convertAndBack(dataSection.getBiomeTag(), (tag) -> { + WalkerUtils.convertList(MCTypeRegistry.BIOME, new NBTMapType(tag), "palette", encodedCurrentVersion, encodedNewVersion); + }); + + sections[i] = new SlimeChunkSectionSkeleton( + blockStateTag, + biomeTag, + dataSection.getBlockLight(), + dataSection.getSkyLight() + ); + + } + chunks.put(chunkPos, new SlimeChunkSkeleton( + chunk.getX(), + chunk.getZ(), + sections, + chunk.getHeightMaps(), + blockEntities, + entities, + chunk.getExtraData(), + chunk.getUpgradeData() + )); + + } + + return new SkeletonSlimeWorld( + data.getName(), + data.getLoader(), + data.isReadOnly(), + chunks, + data.getExtraData(), + data.getPropertyMap(), + newVersion + ); + } + + @Override + public SlimeWorld applyDataFixers(SlimeWorld world) { + return readFromData(world); + } + + private static CompoundBinaryTag convertAndBack(CompoundBinaryTag value, Consumer acceptor) { + if (value == null) return null; + + net.minecraft.nbt.CompoundTag converted = (net.minecraft.nbt.CompoundTag) Converter.convertTag(value); + acceptor.accept(converted); + + return Converter.convertTag(converted); + } + + @Override + public CompoundBinaryTag convertChunkTo1_13(CompoundBinaryTag tag) { + CompoundTag nmsTag = (CompoundTag) Converter.convertTag(tag); + + int version = nmsTag.getInt("DataVersion"); + + long encodedNewVersion = DataConverter.encodeVersions(1631, Integer.MAX_VALUE); + long encodedCurrentVersion = DataConverter.encodeVersions(version, Integer.MAX_VALUE); + + MCTypeRegistry.CHUNK.convert(new NBTMapType(nmsTag), encodedCurrentVersion, encodedNewVersion); + + return Converter.convertTag(nmsTag); + } + + @Override + public List convertEntities(List input, int from, int to) { + List entities = new ArrayList<>(input.size()); + + long encodedNewVersion = DataConverter.encodeVersions(to, Integer.MAX_VALUE); + long encodedCurrentVersion = DataConverter.encodeVersions(from, Integer.MAX_VALUE); + + for (CompoundBinaryTag upgradeEntity : input) { + entities.add( + convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) + ); + } + return entities; + } + + @Override + public List convertTileEntities(List input, int from, int to) { + List blockEntities = new ArrayList<>(input.size()); + + long encodedNewVersion = DataConverter.encodeVersions(to, Integer.MAX_VALUE); + long encodedCurrentVersion = DataConverter.encodeVersions(from, Integer.MAX_VALUE); + + for (CompoundBinaryTag upgradeEntity : input) { + blockEntities.add( + convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.TILE_ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) + ); + } + return blockEntities; + } + + @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); + + ListTag nbtList = (ListTag) Converter.convertTag(input); + 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), + encodedCurrentVersion, encodedNewVersion); + if (replace != null) { + listType.setMap(i, replace); + } + } + + return Converter.convertTag(listType.getTag()); + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java b/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java new file mode 100644 index 000000000..65f812185 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/SlimeNMSBridgeImpl.java @@ -0,0 +1,238 @@ +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; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; +import com.infernalsuite.asp.level.SlimeBootstrap; +import com.infernalsuite.asp.level.SlimeInMemoryWorld; +import com.infernalsuite.asp.level.SlimeLevelInstance; +import com.mojang.serialization.Lifecycle; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.minecraft.SharedConstants; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldLoader; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.storage.CommandStorage; +import net.minecraft.world.level.storage.DimensionDataStorage; +import net.minecraft.world.level.storage.PrimaryLevelData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +public class SlimeNMSBridgeImpl implements SlimeNMSBridge { + + private static final CraftPersistentDataTypeRegistry REGISTRY = new CraftPersistentDataTypeRegistry(); + private static final SimpleDataFixerConverter DATA_FIXER_CONVERTER = new SimpleDataFixerConverter(); + + private SlimeWorld defaultWorld; + private SlimeWorld defaultNetherWorld; + private SlimeWorld defaultEndWorld; + + public static SlimeNMSBridgeImpl instance() { + return (SlimeNMSBridgeImpl) SlimeNMSBridge.instance(); + } + + @Override + public void extractCraftPDC(PersistentDataContainer source, CompoundBinaryTag.Builder builder) { + if (source instanceof CraftPersistentDataContainer craftPDC) { + craftPDC.getRaw().forEach((key, nmsTag) -> builder.put(key, Converter.convertTag(nmsTag))); + } else { + throw new IllegalArgumentException("PersistentDataContainer is not a CraftPersistentDataContainer"); + } + } + + @Override + public SlimeDataConverter getSlimeDataConverter() { + return DATA_FIXER_CONVERTER; + } + + @Override + public boolean loadOverworldOverride() { + if (defaultWorld == null) { + return false; + } + + // See MinecraftServer loading logic + // Some stuff is needed when loading overworld world + SlimeLevelInstance instance = ((SlimeInMemoryWorld) this.loadInstance(defaultWorld, Level.OVERWORLD)).getInstance(); + DimensionDataStorage worldpersistentdata = instance.getDataStorage(); + instance.getCraftServer().scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(instance.getServer(), instance.getScoreboard()); + instance.getServer().commandStorage = new CommandStorage(worldpersistentdata); + + return true; + } + + @Override + public boolean loadNetherOverride() { + if (defaultNetherWorld == null) { + return false; + } + + this.loadInstance(defaultNetherWorld, Level.NETHER); + + return true; + } + + @Override + public boolean loadEndOverride() { + if (defaultEndWorld == null) { + return false; + } + + this.loadInstance(defaultEndWorld, Level.END); + + return true; + } + + /** + * Sets the default worlds for the server.
+ * NOTE: These worlds should be unloaded! + * @param normalWorld The default overworld + * @param netherWorld The default nether + * @param endWorld The default end + */ + @Override + public void setDefaultWorlds(SlimeWorld normalWorld, SlimeWorld netherWorld, SlimeWorld endWorld) { + if (normalWorld != null) { + normalWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.NORMAL.toString().toLowerCase()); + defaultWorld = normalWorld; + } + + if (netherWorld != null) { + netherWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.NETHER.toString().toLowerCase()); + defaultNetherWorld = netherWorld; + } + + if (endWorld != null) { + endWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.THE_END.toString().toLowerCase()); + defaultEndWorld = endWorld; + } + + } + + @Override + public SlimeWorldInstance loadInstance(SlimeWorld slimeWorld) { + return this.loadInstance(slimeWorld, null); + } + + public SlimeWorldInstance loadInstance(SlimeWorld slimeWorld, @Nullable ResourceKey dimensionOverride) { + String worldName = slimeWorld.getName(); + + if (Bukkit.getWorld(worldName) != null) { + throw new IllegalArgumentException("World " + worldName + " already exists! Maybe it's an outdated SlimeWorld object?"); + } + + SlimeLevelInstance server = createCustomWorld(slimeWorld, dimensionOverride); + registerWorld(server); + return server.getSlimeInstance(); + } + + @Override + public SlimeWorldInstance getInstance(World world) { + CraftWorld craftWorld = (CraftWorld) world; + + if (!(craftWorld.getHandle() instanceof SlimeLevelInstance worldServer)) { + return null; + } + + return worldServer.getSlimeInstance(); + } + + + @Override + public int getCurrentVersion() { + return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + } + + public void registerWorld(SlimeLevelInstance server) { + MinecraftServer mcServer = MinecraftServer.getServer(); + mcServer.initWorld(server, server.serverLevelData, mcServer.getWorldData(), server.serverLevelData.worldGenOptions()); + + mcServer.addLevel(server); + } + + private SlimeLevelInstance createCustomWorld(SlimeWorld world, @Nullable ResourceKey dimensionOverride) { + SlimeBootstrap bootstrap = new SlimeBootstrap(world); + String worldName = world.getName(); + + PrimaryLevelData worldDataServer = createWorldData(world); + World.Environment environment = getEnvironment(world); + ResourceKey dimension = switch (environment) { + case NORMAL -> LevelStem.OVERWORLD; + case NETHER -> LevelStem.NETHER; + case THE_END -> LevelStem.END; + default -> throw new IllegalArgumentException("Unknown dimension supplied"); + }; + + ResourceKey worldKey = dimensionOverride == null ? ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(worldName.toLowerCase(Locale.ENGLISH))) : dimensionOverride; + LevelStem stem = MinecraftServer.getServer().registries().compositeAccess().lookupOrThrow(Registries.LEVEL_STEM).get(dimension).orElseThrow().value(); + + SlimeLevelInstance level; + + try { + level = new SlimeLevelInstance(bootstrap, worldDataServer, worldKey, dimension, stem, environment); + } catch (IOException ex) { + throw new RuntimeException(ex); // TODO do something better with this? + } + + // level.setReady(true); + level.setSpawnSettings(world.getPropertyMap().getValue(SlimeProperties.ALLOW_MONSTERS)); + + CompoundTag nmsExtraData = (CompoundTag) Converter.convertTag(CompoundBinaryTag.from(world.getExtraData())); + + //Attempt to read PDC + if (nmsExtraData.get("BukkitValues") != null) level.getWorld().readBukkitValues(nmsExtraData.get("BukkitValues")); + + return level; + } + + private World.Environment getEnvironment(SlimeWorld world) { + return World.Environment.valueOf(world.getPropertyMap().getValue(SlimeProperties.ENVIRONMENT).toUpperCase()); + } + + private PrimaryLevelData createWorldData(SlimeWorld world) { + MinecraftServer mcServer = MinecraftServer.getServer(); + DedicatedServerProperties serverProps = ((DedicatedServer) mcServer).getProperties(); + String worldName = world.getName(); + WorldLoader.DataLoadContext context = mcServer.worldLoader; + + LevelSettings worldsettings = new LevelSettings(worldName, serverProps.gamemode, false, serverProps.difficulty, + true, new GameRules(context.dataConfiguration().enabledFeatures()), mcServer.worldLoader.dataConfiguration()); + + WorldOptions worldoptions = new WorldOptions(0, false, false); + + PrimaryLevelData data = new PrimaryLevelData(worldsettings, worldoptions, PrimaryLevelData.SpecialWorldProperty.FLAT, Lifecycle.stable()); + data.checkName(worldName); + data.setModdedInfo(mcServer.getServerModName(), mcServer.getModdedStatus().shouldReportAsModified()); + data.setInitialized(true); + + return data; + } + +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java b/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java new file mode 100644 index 000000000..9087e71d5 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/config/SlimePaperWorldConfig.java @@ -0,0 +1,56 @@ +package com.infernalsuite.asp.config; + +import io.papermc.paper.configuration.Configurations; +import io.papermc.paper.configuration.PaperConfigurations; +import io.papermc.paper.configuration.WorldConfiguration; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.GameRules; +import org.spigotmc.SpigotWorldConfig; + +import java.nio.file.Path; + +public class SlimePaperWorldConfig { + + private static final ResourceLocation FAKE_WORLD_KEY = ResourceLocation.fromNamespaceAndPath("infernalsuite", "asp-slimeworld"); + public static WorldConfiguration cachedSlimeWorldConfig; + + private SlimePaperWorldConfig() {} + + public static WorldConfiguration initializeOrGet() { + if(cachedSlimeWorldConfig != null) + return cachedSlimeWorldConfig; + + + initialize(MinecraftServer.getServer().paperConfigurations, MinecraftServer.getServer()); + return cachedSlimeWorldConfig; + } + + private static void initialize( + PaperConfigurations paperConfigurations, + MinecraftServer server + ) { + SpigotWorldConfig spigotWorldConfig = new SpigotWorldConfig("asp-slimeworld"); + + GameRules gameRules = new GameRules(server.worldLoader.dataConfiguration().enabledFeatures()); + + Configurations.ContextMap contextMap = PaperConfigurations.createWorldContextMap( + /* + * This might break if Paper team decides to extend/edit the functionality of this the world path property. + * But the goal is for paper to treat the supplied folder as a world folder and create a paper-world.yml file there. + * + * Users can edit this file to change the config for all slime worlds. + */ + Path.of("config", "advancedslimepaper"), + + "asp-slimeworld", + FAKE_WORLD_KEY, + spigotWorldConfig, + server.registryAccess(), + gameRules + ); + cachedSlimeWorldConfig = paperConfigurations.createWorldConfig(contextMap); + } + + +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/ChunkDataLoadTask.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/ChunkDataLoadTask.java new file mode 100644 index 000000000..132547558 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/ChunkDataLoadTask.java @@ -0,0 +1,104 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; +import ca.spottedleaf.concurrentutil.util.Priority; +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.infernalsuite.asp.api.world.SlimeChunk; +import com.mojang.logging.LogUtils; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ImposterProtoChunk; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.ticks.LevelChunkTicks; +import org.slf4j.Logger; + +import java.util.function.Consumer; + +public final class ChunkDataLoadTask implements CommonLoadTask { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + + private final ChunkTaskScheduler scheduler; + private final ServerLevel world; + private final int chunkX; + private final int chunkZ; + private Consumer> onRun; + + private PrioritisedExecutor.PrioritisedTask task; + + private final ChunkLoadTask chunkLoadTask; + + protected ChunkDataLoadTask(ChunkLoadTask chunkLoadTask, final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, + final int chunkZ, final Priority priority, final Consumer> onRun) { + this.chunkLoadTask = chunkLoadTask; + this.scheduler = scheduler; + this.world = world; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.onRun = onRun; + + this.task = this.scheduler.createChunkTask(this.chunkX, this.chunkZ, () -> { + try { + SlimeChunk chunk = this.world.slimeInstance.getChunk(this.chunkX, this.chunkZ); + this.onRun.accept(new GenericDataLoadTask.TaskResult<>(runOnMain(chunk), null)); + } catch (final Exception e) { + LOGGER.error("ERROR", e); + this.onRun.accept(new GenericDataLoadTask.TaskResult<>(null, e)); + } + }, priority); + } + + private ChunkAccess getEmptyChunk() { + LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); + LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); + + return new ImposterProtoChunk(new LevelChunk(this.world, new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, + 0L, null, chunk -> {}, null), true); + } + + private ChunkAccess runOnMain(final SlimeChunk data) { + try { + LevelChunk chunk = this.world.slimeInstance.createChunk(chunkX, chunkZ, data); + return new ImposterProtoChunk(chunk, false); + } catch (final Exception e) { + LOGGER.error("Failed to parse main tasks for task {}, chunk data will be lost", this, e); + return this.getEmptyChunk(); + } + } + + @Override + public Priority getPriority() { + return this.task.getPriority(); + } + + @Override + public void setPriority(Priority priority) { + this.task.setPriority(priority); + } + + @Override + public void raisePriority(Priority priority) { + this.task.raisePriority(priority); + } + + @Override + public void lowerPriority(Priority priority) { + this.task.lowerPriority(priority); + } + + @Override + public boolean cancel() { + return this.task.cancel(); + } + + public boolean schedule(boolean schedule) { + this.scheduler.scheduleChunkTask(chunkX, chunkZ, this.task::execute); + return true; + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/CommonLoadTask.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/CommonLoadTask.java new file mode 100644 index 000000000..980b11942 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/CommonLoadTask.java @@ -0,0 +1,19 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; +import ca.spottedleaf.concurrentutil.util.Priority; + +public interface CommonLoadTask { + + boolean schedule(boolean schedule); + + Priority getPriority(); + + boolean cancel(); + + void lowerPriority(Priority priority); + + void raisePriority(Priority priority); + + void setPriority(Priority priority); +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/FastChunkPruner.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/FastChunkPruner.java new file mode 100644 index 000000000..bd9f6d564 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/FastChunkPruner.java @@ -0,0 +1,70 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; +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.util.NmsUtil; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; + +public class FastChunkPruner { + + public static boolean canBePruned(SlimeWorld world, LevelChunk chunk) { + return canBePruned(world, chunk, null); + } + + public static boolean canBePruned(SlimeWorld world, LevelChunk chunk, ChunkEntitySlices slices) { + NewChunkHolder chunkHolder = NmsUtil.getChunkHolder(chunk); + + // Kenox + // It's not safe to assume that the chunk can be pruned + // if there isn't a loaded chunk there + if (chunkHolder == null) { + return false; + } + + SlimePropertyMap propertyMap = world.getPropertyMap(); + if (propertyMap.getValue(SlimeProperties.SHOULD_LIMIT_SAVE)) { + int minX = propertyMap.getValue(SlimeProperties.SAVE_MIN_X); + int maxX = propertyMap.getValue(SlimeProperties.SAVE_MAX_X); + + int minZ = propertyMap.getValue(SlimeProperties.SAVE_MIN_Z); + int maxZ = propertyMap.getValue(SlimeProperties.SAVE_MAX_Z); + + int chunkX = chunk.locX; + int chunkZ = chunk.locZ; + + if (chunkX < minX || chunkX > maxX) { + return true; + } + + if (chunkZ < minZ || chunkZ > maxZ) { + return true; + } + } + + String pruningSetting = world.getPropertyMap().getValue(SlimeProperties.CHUNK_PRUNING); + if (pruningSetting.equals("aggressive")) { + if(slices == null) { + //in case no slices were provided, try getting them from the chunk holder + slices = chunkHolder.getEntityChunk(); + } + + return chunk.blockEntities.isEmpty() && (slices == null || slices.isEmpty()) && areSectionsEmpty(chunk); + } + + return false; + } + + private static boolean areSectionsEmpty(LevelChunk chunk) { + for (LevelChunkSection section : chunk.getSections()) { + if (!section.hasOnlyAir()) { + return false; + } + } + + return true; + } +} 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 new file mode 100644 index 000000000..ccbc1ea4f --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/NMSSlimeChunk.java @@ -0,0 +1,202 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; +import com.infernalsuite.asp.Converter; +import com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton; +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.util.NmsUtil; +import com.mojang.serialization.Codec; +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.LongArrayBinaryTag; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.LightLayer; +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.PalettedContainerRO; +import net.minecraft.world.level.chunk.storage.SerializableChunkData; +import net.minecraft.world.level.lighting.LevelLightEngine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class NMSSlimeChunk implements SlimeChunk { + private static final Logger LOGGER = LoggerFactory.getLogger(NMSSlimeChunk.class); + + private static final CompoundBinaryTag EMPTY_BLOCK_STATE_PALETTE; + private static final CompoundBinaryTag EMPTY_BIOME_PALETTE; + + // Optimized empty section serialization + static { + { + PalettedContainer empty = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); + Tag tag = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); + + EMPTY_BLOCK_STATE_PALETTE = Converter.convertTag(tag); + } + { + Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME); + PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); + Tag tag = SerializableChunkData.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); + + EMPTY_BIOME_PALETTE = Converter.convertTag(tag); + } + } + + private LevelChunk chunk; + private final Map extra; + private final CompoundBinaryTag upgradeData; + + public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference) { + this.chunk = chunk; + this.extra = reference == null ? new HashMap<>() : reference.getExtraData(); + this.upgradeData = reference == null ? null : reference.getUpgradeData(); + } + + public void updatePersistentDataContainer() { + this.extra.put("ChunkBukkitValues", Converter.convertTag(chunk.persistentDataContainer.toTagCompound())); + } + + @Override + public int getX() { + return chunk.getPos().x; + } + + @Override + public int getZ() { + return chunk.getPos().z; + } + + @Override + public SlimeChunkSection[] getSections() { + SlimeChunkSection[] sections = new SlimeChunkSection[this.chunk.getSectionsCount()]; + LevelLightEngine lightEngine = chunk.getLevel().getChunkSource().getLightEngine(); + + Registry biomeRegistry = chunk.biomeRegistry; + + Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow()); + + for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { + LevelChunkSection section = chunk.getSections()[sectionId]; + // Sections CANNOT be null in 1.18 + + // Block Light Nibble Array + NibbleArray blockLightArray = Converter.convertArray(lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunk.getPos(), sectionId))); + + // Sky light Nibble Array + NibbleArray skyLightArray = Converter.convertArray(lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunk.getPos(), sectionId))); + + // Block Data + CompoundBinaryTag blockStateTag; + if (section.hasOnlyAir()) { + blockStateTag = EMPTY_BLOCK_STATE_PALETTE; + } else { + Tag data = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling + blockStateTag = Converter.convertTag(data); + } + + + CompoundBinaryTag biomeTag; + PalettedContainer> biomes = (PalettedContainer>) section.getBiomes(); + if (biomes.data.palette().getSize() == 1 && biomes.data.palette().maybeHas((h) -> h.is(Biomes.PLAINS))) { + biomeTag = EMPTY_BIOME_PALETTE; + } else { + Tag biomeData = codec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(); // todo error handling + biomeTag = Converter.convertTag(biomeData); + } + + sections[sectionId] = new SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); + } + + return sections; + } + + @Override + public CompoundBinaryTag getHeightMaps() { + CompoundBinaryTag.Builder heightMapsTagBuilder = CompoundBinaryTag.builder(); + + this.chunk.heightmaps.forEach((type, map) -> { + if (type.keepAfterWorldgen()) { + heightMapsTagBuilder.put(type.name(), LongArrayBinaryTag.longArrayBinaryTag(map.getRawData())); + } + }); + + return heightMapsTagBuilder.build(); + } + + @Override + public List getTileEntities() { + Collection blockEntities = this.chunk.blockEntities.values(); + List tileEntities = new ArrayList<>(blockEntities.size()); + + for (BlockEntity entity : blockEntities) { + CompoundTag entityNbt = entity.saveWithFullMetadata(net.minecraft.server.MinecraftServer.getServer().registryAccess()); + tileEntities.add(Converter.convertTag(entityNbt)); + } + + return tileEntities; + } + + @Override + public List getEntities() { + NewChunkHolder chunkHolder = NmsUtil.getChunkHolder(chunk); + if(chunkHolder == null) return new ArrayList<>(); + + ChunkEntitySlices slices = chunkHolder.getEntityChunk(); + return getEntities(slices); + } + + 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); + } + } + + return entities; + } + + @Override + public Map getExtraData() { + return extra; + } + + @Override + public CompoundBinaryTag getUpgradeData() { + return upgradeData; + } + + public LevelChunk getChunk() { + return chunk; + } + + public void setChunk(LevelChunk chunk) { + this.chunk = chunk; + } + +} 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 new file mode 100644 index 000000000..f58a673d0 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/ReadOnlyDimensionDataStorage.java @@ -0,0 +1,44 @@ +package com.infernalsuite.asp.level; + +import com.mojang.datafixers.DataFixer; +import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.storage.DimensionDataStorage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +/* + * This dimension data storage does not serialize and/or load from disk. + */ +public class ReadOnlyDimensionDataStorage extends DimensionDataStorage { + + public ReadOnlyDimensionDataStorage(Path dataFolder, DataFixer fixerUpper, HolderLookup.Provider registries) { + super(dataFolder, fixerUpper, registries); + } + + @SuppressWarnings("unchecked") + @Override + public @Nullable T get(SavedData.@NotNull Factory factory, @NotNull String name) { + Optional optional = this.cache.get(name); + if(optional == null) { + return null; + } + return (T) optional.orElse(null); + } + + @Override + public @NotNull CompletableFuture scheduleSave() { + return CompletableFuture.completedFuture(null); + } + + @Override + public void saveAndJoin() {} + + @Override + public void close() {} + +} 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 new file mode 100644 index 000000000..f2b41f803 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SafeNmsChunkWrapper.java @@ -0,0 +1,103 @@ +package com.infernalsuite.asp.level; + +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; + +import java.util.List; +import java.util.Map; + +public class SafeNmsChunkWrapper implements SlimeChunk { + + private final NMSSlimeChunk wrapper; + private final SlimeChunk safety; + + public SafeNmsChunkWrapper(NMSSlimeChunk wrapper, SlimeChunk safety) { + this.wrapper = wrapper; + this.safety = safety; + } + + @Override + public int getX() { + return this.wrapper.getX(); + } + + @Override + public int getZ() { + return this.wrapper.getZ(); + } + + @Override + public SlimeChunkSection[] getSections() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getSections(); + } + + return this.wrapper.getSections(); + } + + @Override + public CompoundBinaryTag getHeightMaps() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getHeightMaps(); + } + + return this.wrapper.getHeightMaps(); + } + + @Override + public List getTileEntities() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getTileEntities(); + } + + return this.wrapper.getTileEntities(); + } + + @Override + public List getEntities() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getEntities(); + } + + return this.wrapper.getEntities(); + } + + @Override + public Map getExtraData() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getExtraData(); + } + + return this.wrapper.getExtraData(); + } + + @Override + public CompoundBinaryTag getUpgradeData() { + if (shouldDefaultBackToSlimeChunk()) { + return this.safety.getUpgradeData(); + } + + return this.wrapper.getUpgradeData(); + } + + /* +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; + } + + public NMSSlimeChunk getWrapper() { + return wrapper; + } + + public SlimeChunk getSafety() { + return safety; + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeBootstrap.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeBootstrap.java new file mode 100644 index 000000000..5e4e77384 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeBootstrap.java @@ -0,0 +1,8 @@ +package com.infernalsuite.asp.level; + +import com.infernalsuite.asp.api.world.SlimeWorld; + +public record SlimeBootstrap( + SlimeWorld initial +) { +} 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 new file mode 100644 index 000000000..39e677a39 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkConverter.java @@ -0,0 +1,154 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray; +import ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine; +import com.infernalsuite.asp.Converter; +import com.infernalsuite.asp.api.utils.NibbleArray; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +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 java.util.EnumSet; +import java.util.List; + +public class SlimeChunkConverter { + + static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeChunk chunk) { + int x = chunk.getX(); + int z = chunk.getZ(); + + ChunkPos pos = new ChunkPos(x, z); + + // Chunk sections + LevelChunkSection[] sections = new LevelChunkSection[instance.getSectionsCount()]; + + SWMRNibbleArray[] blockNibbles = StarLightEngine.getFilledEmptyLight(instance); + SWMRNibbleArray[] skyNibbles = StarLightEngine.getFilledEmptyLight(instance); + instance.getServer().scheduleOnMain(() -> { + //TODO: Figure out if this is important. Seems empty in paper + instance.getLightEngine().retainData(pos, true); + }); + + Registry biomeRegistry = instance.registryAccess().lookupOrThrow(Registries.BIOME); + + Codec>> codec = PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), + biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow(), null); + + for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { + SlimeChunkSection slimeSection = chunk.getSections()[sectionId]; + + if (slimeSection != null) { + NibbleArray blockLight = slimeSection.getBlockLight(); + if (blockLight != null) { + blockNibbles[sectionId] = new SWMRNibbleArray(blockLight.getBacking()); + } + + NibbleArray skyLight = slimeSection.getSkyLight(); + if (skyLight != null) { + skyNibbles[sectionId] = new SWMRNibbleArray(skyLight.getBacking()); + } + + PalettedContainer blockPalette; + if (slimeSection.getBlockStatesTag() != null) { + DataResult> dataresult = SerializableChunkData.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { + System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging + }); + blockPalette = dataresult.getOrThrow(); // todo proper logging + } else { + blockPalette = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); + } + + PalettedContainer> biomePalette; + + if (slimeSection.getBiomeTag() != null) { + DataResult>> dataresult = codec.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBiomeTag())).promotePartial((s) -> { + System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging + }); + biomePalette = dataresult.getOrThrow(); // todo proper logging + } else { + biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); + } + + if (sectionId < sections.length) { + LevelChunkSection section = new LevelChunkSection(blockPalette, biomePalette); + sections[sectionId] = section; + } + } + } + + LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); + LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); + UpgradeData upgradeData; + if (chunk.getUpgradeData() != null) { + upgradeData = new UpgradeData((net.minecraft.nbt.CompoundTag) Converter.convertTag(chunk.getUpgradeData()), instance); + } else { + upgradeData = UpgradeData.EMPTY; + } + + LevelChunk.PostLoadProcessor processor = SerializableChunkData.postLoadChunk( + instance, + chunk.getEntities().stream().map(tag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)).toList(), + chunk.getTileEntities().stream().map(tag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)).toList() + ); + + SlimeChunkLevel nmsChunk = new SlimeChunkLevel(instance, chunk, pos, upgradeData, blockLevelChunkTicks, + fluidLevelChunkTicks, 0L, sections, processor, null); + + // Height Maps + EnumSet heightMapTypes = nmsChunk.getPersistedStatus().heightmapsAfter(); + CompoundBinaryTag heightMaps = chunk.getHeightMaps(); + EnumSet unsetHeightMaps = EnumSet.noneOf(Heightmap.Types.class); + + // Light + nmsChunk.starlight$setBlockNibbles(blockNibbles); + nmsChunk.starlight$setSkyNibbles(skyNibbles); + + for (Heightmap.Types type : heightMapTypes) { + String name = type.getSerializedName(); + + long[] heightMap = heightMaps.getLongArray(name); + if (heightMap.length > 0) { + nmsChunk.setHeightmap(type, heightMap); + } else { + unsetHeightMaps.add(type); + } + } + + // Don't try to populate heightmaps if there are none. + // Does a crazy amount of block lookups + if (!unsetHeightMaps.isEmpty()) { + Heightmap.primeHeightmaps(nmsChunk, unsetHeightMaps); + } + + // Attempt to read PDC from the extra tag + if (chunk.getExtraData().containsKey("ChunkBukkitValues")) { + nmsChunk.persistentDataContainer.putAll((CompoundTag) Converter.convertTag(chunk.getExtraData().get("ChunkBukkitValues"))); + } + + return nmsChunk; + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkLevel.java b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkLevel.java new file mode 100644 index 000000000..afa58f69b --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeChunkLevel.java @@ -0,0 +1,55 @@ +package com.infernalsuite.asp.level; + +import com.infernalsuite.asp.api.world.SlimeChunk; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.ticks.LevelChunkTicks; +import org.jetbrains.annotations.Nullable; + +public class SlimeChunkLevel extends LevelChunk { + + private final SlimeInMemoryWorld inMemoryWorld; + private final NMSSlimeChunk nmsSlimeChunk; + private final @Nullable SlimeChunk slimeReference; + + public SlimeChunkLevel( + SlimeLevelInstance world, + @Nullable SlimeChunk reference, + ChunkPos pos, + UpgradeData upgradeData, + LevelChunkTicks blockTickScheduler, + LevelChunkTicks fluidTickScheduler, + long inhabitedTime, + @Nullable LevelChunkSection[] sectionArrayInitializer, + @Nullable LevelChunk.PostLoadProcessor entityLoader, + @Nullable BlendingData blendingData + ) { + super(world, pos, upgradeData, blockTickScheduler, fluidTickScheduler, inhabitedTime, sectionArrayInitializer, entityLoader, blendingData); + this.inMemoryWorld = world.slimeInstance; + this.nmsSlimeChunk = new NMSSlimeChunk(this, reference); + this.slimeReference = reference; + } + + @Override + public void loadCallback() { + //Not the earliest point where we can do promote the chunk in storage, but it's the easiest without any further patches, + //and without causing a potential memory leak, and It's where bukkit calls its chunk load event so we should be fine. + this.inMemoryWorld.promoteInChunkStorage(this); + + super.loadCallback(); + } + + public SlimeChunk getSafeSlimeReference() { + if(this.slimeReference == null) return this.nmsSlimeChunk; + return new SafeNmsChunkWrapper(this.nmsSlimeChunk, this.slimeReference); + } + + public NMSSlimeChunk getNmsSlimeChunk() { + return nmsSlimeChunk; + } +} 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 new file mode 100644 index 000000000..f42eb9ffd --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeInMemoryWorld.java @@ -0,0 +1,282 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; +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.pdc.AdventurePersistentDataContainer; +import com.infernalsuite.asp.serialization.slime.SlimeSerializer; +import com.infernalsuite.asp.skeleton.SkeletonCloning; +import com.infernalsuite.asp.skeleton.SkeletonSlimeWorld; +import com.infernalsuite.asp.skeleton.SlimeChunkSkeleton; +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.SharedConstants; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.ticks.LevelChunkTicks; +import org.bukkit.World; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentMap; + +/* +The concept of this is a bit flawed, since ideally this should be a 1:1 representation of the MC world. +However, due to the complexity of the chunk system we essentially need to wrap around it. +This stores slime chunks, and when unloaded, will properly convert it to a normal slime chunk for storage. + */ +public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { + + private final SlimeLevelInstance instance; + + private final ConcurrentMap extra; + private final AdventurePersistentDataContainer extraPDC; + private final SlimePropertyMap propertyMap; + private final SlimeLoader loader; + + private final Long2ObjectMap chunkStorage = new Long2ObjectOpenHashMap<>(); + private boolean readOnly; + + public SlimeInMemoryWorld(SlimeBootstrap bootstrap, SlimeLevelInstance instance) { + this.instance = instance; + this.extra = bootstrap.initial().getExtraData(); + this.propertyMap = bootstrap.initial().getPropertyMap(); + this.loader = bootstrap.initial().getLoader(); + this.readOnly = bootstrap.initial().isReadOnly(); + + for (SlimeChunk initial : bootstrap.initial().getChunkStorage()) { + long pos = Util.chunkPosition(initial.getX(), initial.getZ()); + + this.chunkStorage.put(pos, initial); + } + + this.extraPDC = new AdventurePersistentDataContainer(this.extra); + } + + @Override + public String getName() { + return this.instance.getMinecraftWorld().serverLevelData.getLevelName(); + } + + @Override + public SlimeLoader getLoader() { + return this.loader; + } + + public LevelChunk createChunk(int x, int z, SlimeChunk chunk) { + SlimeChunkLevel levelChunk; + if (chunk == null) { + net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(x, z); + LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); + LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); + + levelChunk = new SlimeChunkLevel(this.instance, null, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, + 0L, null, null, null); + + //Make SlimeProperties.DEFAULT_BIOME work + levelChunk.fillBiomesFromNoise(instance.chunkSource.getGenerator().getBiomeSource(), + instance.chunkSource.randomState().sampler()); + + } else { + levelChunk = SlimeChunkConverter.deserializeSlimeChunk(this.instance, chunk); + } + + //Don't put this chunk in chunkStorage yet, as creating a chunk does not guarantee it is loaded in the future. + + return levelChunk; + } + + // Authored by: Kenox + // Don't use the NMS live chunk in the chunk map + public void unload(LevelChunk providedChunk, ChunkEntitySlices slices) { + final int x = providedChunk.locX; + final int z = providedChunk.locZ; + + if (FastChunkPruner.canBePruned(this, providedChunk, slices)) { + this.chunkStorage.remove(Util.chunkPosition(x, z)); + return; + } + NMSSlimeChunk chunk; + if(providedChunk instanceof SlimeChunkLevel slimeChunkLevel) { + chunk = slimeChunkLevel.getNmsSlimeChunk(); + } else { + chunk = new NMSSlimeChunk(providedChunk, getChunk(x, z)); + } + chunk.updatePersistentDataContainer(); + + this.chunkStorage.put(Util.chunkPosition(x, z), new SlimeChunkSkeleton( + chunk.getX(), + chunk.getZ(), + chunk.getSections(), + chunk.getHeightMaps(), + chunk.getTileEntities(), + chunk.getEntities(slices), //As the slices are not available anymore, we can't get the entities otherwise + chunk.getExtraData(), + null + )); + } + + @Override + public SlimeChunk getChunk(int x, int z) { + return this.chunkStorage.get(Util.chunkPosition(x, z)); + } + + @Override + public Collection getChunkStorage() { + return this.chunkStorage.values(); + } + + @Override + public @NotNull World getBukkitWorld() { + return this.instance.getWorld(); + } + + @Override + public SlimeWorld getSerializableCopy() { + SlimeWorld world = SkeletonCloning.weakCopy(this); + + Long2ObjectMap cloned = new Long2ObjectOpenHashMap<>(); + for (Long2ObjectMap.Entry entry : this.chunkStorage.long2ObjectEntrySet()) { + SlimeChunk clonedChunk = entry.getValue(); + // NMS "live" chunks need to be converted + { + LevelChunk chunk = null; + if (clonedChunk instanceof SafeNmsChunkWrapper safeNmsChunkWrapper) { + if (safeNmsChunkWrapper.shouldDefaultBackToSlimeChunk()) { + clonedChunk = safeNmsChunkWrapper.getSafety(); + } else { + chunk = safeNmsChunkWrapper.getWrapper().getChunk(); + } + } else if (clonedChunk instanceof NMSSlimeChunk nmsSlimeChunk) { + chunk = nmsSlimeChunk.getChunk(); + } + + if (chunk != null) { + if (FastChunkPruner.canBePruned(world, chunk)) { + continue; + } + + // Serialize Bukkit Values (PDC) + + CompoundBinaryTag adventureTag = Converter.convertTag(chunk.persistentDataContainer.toTagCompound()); + clonedChunk.getExtraData().put("ChunkBukkitValues", adventureTag); + + clonedChunk = new SlimeChunkSkeleton( + clonedChunk.getX(), + clonedChunk.getZ(), + clonedChunk.getSections(), + clonedChunk.getHeightMaps(), + clonedChunk.getTileEntities(), + clonedChunk.getEntities(), + clonedChunk.getExtraData(), + clonedChunk.getUpgradeData() + ); + } + } + + cloned.put(entry.getLongKey(), clonedChunk); + } + + // Serialize Bukkit Values (PDC) + + var nmsTag = new net.minecraft.nbt.CompoundTag(); + 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")); + world.getExtraData().put("BukkitValues", adventureTag); + + return new SkeletonSlimeWorld(world.getName(), + world.getLoader(), + world.isReadOnly(), + cloned, + world.getExtraData(), + world.getPropertyMap(), + world.getDataVersion() + ); + } + + @Override + public SlimePropertyMap getPropertyMap() { + return this.propertyMap; + } + + @Override + public boolean isReadOnly() { + return this.getLoader() == null || this.readOnly; + } + + @Override + public SlimeWorld clone(String worldName) { + try { + return clone(worldName, null); + } catch (WorldAlreadyExistsException | IOException ignored) { + return null; // Never going to happen + } + } + + @Override + public SlimeWorld clone(String worldName, SlimeLoader loader) throws WorldAlreadyExistsException, IOException { + if (this.getName().equals(worldName)) { + throw new IllegalArgumentException("The clone world cannot have the same name as the original world!"); + } + + if (worldName == null) { + throw new IllegalArgumentException("The world name cannot be null!"); + } + if (loader != null) { + if (loader.worldExists(worldName)) { + throw new WorldAlreadyExistsException(worldName); + } + } + + //Make new worlds always non-read-only. if the provided loader is null, the fullClone method will set it to true again + SlimeWorld cloned = SkeletonCloning.fullClone(worldName, this, loader, false); + if (loader != null) { + loader.saveWorld(worldName, SlimeSerializer.serialize(cloned)); + } + + return cloned; + } + + @Override + public int getDataVersion() { + return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + } + + @Override + public ConcurrentMap getExtraData() { + return this.extra; + } + + @Override + public Collection getWorldMaps() { + return List.of(); + } + + public SlimeLevelInstance getInstance() { + return instance; + } + + @Override + public @NotNull PersistentDataContainer getPersistentDataContainer() { + return this.extraPDC; + } + + public void promoteInChunkStorage(SlimeChunkLevel chunk) { + chunkStorage.put(Util.chunkPosition(chunk.locX, chunk.locZ), chunk.getSafeSlimeReference()); + } +} 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 new file mode 100644 index 000000000..fcf71851a --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelGenerator.java @@ -0,0 +1,39 @@ +package com.infernalsuite.asp.level; + +import com.mojang.serialization.MapCodec; +import net.minecraft.core.Holder; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class SlimeLevelGenerator extends FlatLevelSource { + + public SlimeLevelGenerator(Holder biome) { + super(new FlatLevelGeneratorSettings(Optional.empty(), biome, List.of()), getSource(biome)); + } + + private static BiomeSource getSource(Holder biome) { + return new BiomeSource() { + @Override + protected MapCodec codec() { + return null; + } + + @Override + protected Stream> collectPossibleBiomes() { + return Stream.of(biome); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler noise) { + return biome; + } + }; + } +} 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 new file mode 100644 index 000000000..c5fbe3a67 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/level/SlimeLevelInstance.java @@ -0,0 +1,226 @@ +package com.infernalsuite.asp.level; + +import ca.spottedleaf.concurrentutil.util.Priority; +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; +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.serialization.slime.SlimeSerializer; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.ProgressListener; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.biome.Biome; +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.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.level.validation.DirectoryValidator; +import org.apache.commons.io.FileUtils; +import org.bukkit.Bukkit; +import org.bukkit.event.world.WorldSaveEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spigotmc.AsyncCatcher; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.logging.Level; + +public class SlimeLevelInstance extends ServerLevel { + + + public static LevelStorageSource CUSTOM_LEVEL_STORAGE; + + static { + try { + Path path = Files.createTempDirectory("swm-" + UUID.randomUUID().toString().substring(0, 5)).toAbsolutePath(); + DirectoryValidator directoryvalidator = LevelStorageSource.parseValidator(path.resolve("allowed_symlinks.txt")); + CUSTOM_LEVEL_STORAGE = new LevelStorageSource(path, path, directoryvalidator, DataFixers.getDataFixer()); + + FileUtils.forceDeleteOnExit(path.toFile()); + + } catch (IOException ex) { + throw new IllegalStateException("Couldn't create dummy file directory.", ex); + } + } + + private static final ExecutorService WORLD_SAVER_SERVICE = Executors.newFixedThreadPool(4, new ThreadFactoryBuilder() + .setNameFormat("SWM Pool Thread #%1$d").build()); + + private final Object saveLock = new Object(); + + public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primaryLevelData, + ResourceKey worldKey, + ResourceKey dimensionKey, LevelStem worldDimension, + org.bukkit.World.Environment environment) throws IOException { + + super(slimeBootstrap, MinecraftServer.getServer(), MinecraftServer.getServer().executor, + CUSTOM_LEVEL_STORAGE.createAccess(slimeBootstrap.initial().getName() + UUID.randomUUID(), dimensionKey), + primaryLevelData, worldKey, worldDimension, + MinecraftServer.getServer().progressListenerFactory.create(11), false, 0, + Collections.emptyList(), true, null, environment, null, null); + this.slimeInstance = new SlimeInMemoryWorld(slimeBootstrap, this); + + + SlimePropertyMap propertyMap = slimeBootstrap.initial().getPropertyMap(); + + this.serverLevelData.setDifficulty(Difficulty.valueOf(propertyMap.getValue(SlimeProperties.DIFFICULTY).toUpperCase())); + this.serverLevelData.setSpawn(new BlockPos( + propertyMap.getValue(SlimeProperties.SPAWN_X), + propertyMap.getValue(SlimeProperties.SPAWN_Y), + propertyMap.getValue(SlimeProperties.SPAWN_Z)), + propertyMap.getValue(SlimeProperties.SPAWN_YAW)); + super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS)); + + this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); + } + + @Override + public @NotNull ChunkGenerator getGenerator(SlimeBootstrap slimeBootstrap) { + 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); + } + + @Override + public void save(@Nullable ProgressListener progressUpdate, boolean forceSave, boolean savingDisabled, boolean close) { + if (!savingDisabled) save(); + } + + public void unload(@NotNull LevelChunk chunk, ChunkEntitySlices slices) { + slimeInstance.unload(chunk, slices); + } + + @Override + public void saveIncrementally(boolean doFull) { + if(doFull) { + //Avoid doing the internal save because it saves the level.dat into the temp folder. That causes pterodactyl users to have issues. + save(); + } + } + + public Future save() { + AsyncCatcher.catchOp("SWM world save"); + try { + if (!this.slimeInstance.isReadOnly() && this.slimeInstance.getLoader() != null) { + Bukkit.getPluginManager().callEvent(new WorldSaveEvent(getWorld())); + + //this.getChunkSource().save(forceSave); + this.serverLevelData.setWorldBorder(this.getWorldBorder().createSettings()); + this.serverLevelData.setCustomBossEvents(MinecraftServer.getServer().getCustomBossEvents().save(MinecraftServer.getServer().registryAccess())); + + if (MinecraftServer.getServer().isStopped()) { // Make sure the world gets saved before stopping the server by running it from the main thread + saveInternal().get(); // Async wait for it to finish + } else { + return this.saveInternal(); + } + } + } catch (Throwable e) { + Bukkit.getLogger().log(Level.SEVERE, "There was a problem saving the SlimeLevelInstance " + serverLevelData.getLevelName(), e); + return CompletableFuture.failedFuture(e); + } + return CompletableFuture.completedFuture(null); + } + + private Future saveInternal() { + synchronized (saveLock) { // Don't want to save the SlimeWorld from multiple threads simultaneously + SlimeWorldInstance slimeWorld = this.slimeInstance; + Bukkit.getLogger().log(Level.INFO, "Saving world " + this.slimeInstance.getName() + "..."); + long start = System.currentTimeMillis(); + + SlimeWorld world = this.slimeInstance.getSerializableCopy(); + return WORLD_SAVER_SERVICE.submit(() -> { + try { + byte[] serializedWorld = SlimeSerializer.serialize(world); + long saveStart = System.currentTimeMillis(); + slimeWorld.getLoader().saveWorld(slimeWorld.getName(), serializedWorld); + Bukkit.getLogger().log(Level.INFO, "World " + slimeWorld.getName() + " serialized in " + (saveStart - start) + "ms and saved in " + (System.currentTimeMillis() - saveStart) + "ms."); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "There was an issue saving world " + slimeWorld.getName() + " asynchronously.", ex); + } + }); + + } + } + + public SlimeWorldInstance getSlimeInstance() { + return this.slimeInstance; + } + + public ChunkDataLoadTask getLoadTask(ChunkLoadTask task, ChunkTaskScheduler scheduler, ServerLevel world, int chunkX, int chunkZ, Priority priority, Consumer> onRun) { + return new ChunkDataLoadTask(task, scheduler, world, chunkX, chunkZ, priority, onRun); + } + + @Override + public void setDefaultSpawnPos(BlockPos pos, float angle) { + super.setDefaultSpawnPos(pos, angle); + + SlimePropertyMap propertyMap = this.slimeInstance.getPropertyMap(); + propertyMap.setValue(SlimeProperties.SPAWN_X, pos.getX()); + propertyMap.setValue(SlimeProperties.SPAWN_Y, pos.getY()); + propertyMap.setValue(SlimeProperties.SPAWN_Z, pos.getZ()); + propertyMap.setValue(SlimeProperties.SPAWN_YAW, angle); + } + + public void deleteTempFiles() { + WORLD_SAVER_SERVICE.execute(() -> { + Path path = this.levelStorageAccess.levelDirectory.path(); + try { + // We do this manually and not use the deleteLevel function as it would cause a level deleted message + // to appear in the log which might be confusing for our users + Files.walkFileTree(path, new SimpleFileVisitor<>() { + @Override + public @NotNull FileVisitResult visitFile(Path file, @NotNull BasicFileAttributes attrs) throws IOException { + if (!file.equals(path)) { + Files.deleteIfExists(file); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public @NotNull FileVisitResult postVisitDirectory(Path dir, @javax.annotation.Nullable IOException exception) throws IOException { + if (exception != null) { + throw exception; + } else { + if (dir.equals(levelStorageAccess.levelDirectory.path())) { + Files.deleteIfExists(path); + } + + Files.deleteIfExists(dir); + return FileVisitResult.CONTINUE; + } + } + }); + } catch (IOException e) { + Bukkit.getLogger().log(Level.WARNING, "Unable to delete temp level directory" , e); + } + }); + } +} diff --git a/aspaper-server/src/main/java/com/infernalsuite/asp/util/NmsUtil.java b/aspaper-server/src/main/java/com/infernalsuite/asp/util/NmsUtil.java new file mode 100644 index 000000000..c62d4c1d1 --- /dev/null +++ b/aspaper-server/src/main/java/com/infernalsuite/asp/util/NmsUtil.java @@ -0,0 +1,52 @@ +package com.infernalsuite.asp.util; + +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; +import com.infernalsuite.asp.InternalPlugin; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.scheduler.CraftScheduler; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.CountDownLatch; + +public class NmsUtil { + + public static long asLong(int chunkX, int chunkZ) { + return (((long) chunkZ) * Integer.MAX_VALUE + ((long) chunkX)); + //return (long)chunkX & 4294967295L | ((long)chunkZ & 4294967295L) << 32; + } + + public static void runSyncAndWait(Runnable runnable) { + if (Bukkit.isPrimaryThread()) { + runnable.run(); + return; + } + + CountDownLatch latch = new CountDownLatch(1); + RuntimeException[] runtimeException = new RuntimeException[1]; + + Bukkit.getScheduler().runTask(new InternalPlugin(), () -> { + try { + runnable.run(); + } catch (RuntimeException e) { + runtimeException[0] = e; + } finally { + latch.countDown(); + } + }); + + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); // Rather propagate the interrupt (and thus prevent further execution) than continue + } + + if (runtimeException[0] != null) { + throw runtimeException[0]; + } + } + + public static NewChunkHolder getChunkHolder(LevelChunk chunk) { + return chunk.level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunk.getPos().x, chunk.getPos().z); + } +} diff --git a/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.AdvancedSlimePaperAPI b/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.AdvancedSlimePaperAPI new file mode 100644 index 000000000..d2fcf45a1 --- /dev/null +++ b/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.AdvancedSlimePaperAPI @@ -0,0 +1 @@ +com.infernalsuite.asp.AdvancedSlimePaper diff --git a/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.SlimeNMSBridge b/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.SlimeNMSBridge new file mode 100644 index 000000000..973f2f48d --- /dev/null +++ b/aspaper-server/src/main/resources/META-INF/services/com.infernalsuite.asp.api.SlimeNMSBridge @@ -0,0 +1 @@ +com.infernalsuite.asp.SlimeNMSBridgeImpl \ No newline at end of file diff --git a/build-data/aspaper.at b/build-data/aspaper.at new file mode 100644 index 000000000..2014669a0 --- /dev/null +++ b/build-data/aspaper.at @@ -0,0 +1,7 @@ +# This file is auto generated, any changes may be overridden! +# See CONTRIBUTING.md on how to add access transformers. +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; diff --git a/build.gradle.kts b/build.gradle.kts index 3f4b9bfb8..6249f1aa3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,53 +1,59 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent + plugins { java - `maven-publish` - id("com.gradleup.shadow") version "8.3.5" apply false - id("io.papermc.paperweight.patcher") version "1.7.5" - id("org.kordamp.gradle.profiles") version "0.54.0" + id("io.papermc.paperweight.patcher") } -val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" +paperweight { + upstreams.paper { + ref = providers.gradleProperty("paperRef") + + // Setup file patches for build scripts + patchFile { + path = "paper-api/build.gradle.kts" + outputFile = file("aspaper-api/build.gradle.kts") + patchFile = file("aspaper-api/build.gradle.kts.patch") + } + patchFile { + path = "paper-server/build.gradle.kts" + outputFile = file("aspaper-server/build.gradle.kts") + patchFile = file("aspaper-server/build.gradle.kts.patch") + } -repositories { - mavenCentral() - maven(paperMavenPublicUrl) { - content { onlyForConfigurations(configurations.paperclip.name) } + patchDir("paperApi") { + upstreamPath = "paper-api" + excludes = setOf("build.gradle.kts") + patchesDir = file("aspaper-api/paper-patches") + outputDir = file("paper-api") + } } } -allprojects { - apply(plugin = "java") +subprojects { + apply(plugin = "java-library") apply(plugin = "maven-publish") - java { + extensions.configure { toolchain { - languageVersion.set(JavaLanguageVersion.of(21)) + languageVersion = JavaLanguageVersion.of(JAVA_VERSION) } } repositories { - mavenLocal() mavenCentral() - - maven("https://repo.papermc.io/repository/maven-public/") - maven("https://repo.codemc.io/repository/nms/") - maven("https://repo.rapture.pw/repository/maven-releases/") - maven("https://repo.glaremasters.me/repository/concuncan/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") - maven("https://oss.sonatype.org/content/repositories/snapshots/") + maven(PAPER_MAVEN_PUBLIC_URL) } -} -dependencies { - remapper("net.fabricmc:tiny-remapper:0.10.3:fat") - decompiler("org.vineflower:vineflower:1.10.1") - paperclip("io.papermc:paperclip:3.0.3") -} - -subprojects { + tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + } tasks.withType { options.encoding = Charsets.UTF_8.name() - options.release.set(21) + options.release = JAVA_VERSION + options.isFork = true } tasks.withType { options.encoding = Charsets.UTF_8.name() @@ -55,35 +61,11 @@ subprojects { tasks.withType { filteringCharset = Charsets.UTF_8.name() } - - repositories { - mavenCentral() - maven(paperMavenPublicUrl) - } -} - -paperweight { - serverProject.set(project(":slimeworldmanager-server")) - - remapRepo.set(paperMavenPublicUrl) - decompileRepo.set(paperMavenPublicUrl) - - usePaperUpstream(providers.gradleProperty("paperRef")) { - withPaperPatcher { - apiPatchDir.set(layout.projectDirectory.dir("patches/api")) - apiOutputDir.set(layout.projectDirectory.dir("slimeworldmanager-api")) - - serverPatchDir.set(layout.projectDirectory.dir("patches/server")) - serverOutputDir.set(layout.projectDirectory.dir("slimeworldmanager-server")) - - patchTasks { - register("generatedApi") { - isBareDirectory.set(true) - upstreamDirPath.set("paper-api-generator/generated") - patchDir.set(layout.projectDirectory.dir("patches/generatedApi")) - outputDir.set(layout.projectDirectory.dir("paper-api-generator/generated")) - } - } + tasks.withType { + testLogging { + showStackTraces = true + exceptionFormat = TestExceptionFormat.FULL + events(TestLogEvent.STANDARD_OUT) } } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..a47ba2a45 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `kotlin-dsl` +} + +group = "com.infernalsuite" + +repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() +} + +fun convertPlugin(plugin: Provider): String { + val id = plugin.get().pluginId + return "$id:$id.gradle.plugin:${plugin.get().version}" +} + +dependencies { + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(convertPlugin(libs.plugins.blossom)) + implementation(convertPlugin(libs.plugins.gitprops)) + implementation(convertPlugin(libs.plugins.profiles)) + implementation(convertPlugin(libs.plugins.kotlin.jvm)) + implementation(convertPlugin(libs.plugins.lombok)) + implementation(convertPlugin(libs.plugins.paperweight.patcher)) + implementation(convertPlugin(libs.plugins.plugin.yml.paper)) + implementation(convertPlugin(libs.plugins.shadow)) +} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 000000000..bbf148c90 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "buildSrc" + +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/asp.base-conventions.gradle.kts b/buildSrc/src/main/kotlin/asp.base-conventions.gradle.kts new file mode 100644 index 000000000..209a239a6 --- /dev/null +++ b/buildSrc/src/main/kotlin/asp.base-conventions.gradle.kts @@ -0,0 +1,58 @@ +plugins { + `java-library` + id("com.gorylenko.gradle-git-properties") +} + +group = rootProject.providers.gradleProperty("group").get() +version = rootProject.providers.gradleProperty("apiVersion").get() + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(JAVA_VERSION)) + } + withJavadocJar() + withSourcesJar() +} + +repositories { + mavenLocal() + mavenCentral() + + maven(PAPER_MAVEN_PUBLIC_URL) + + maven("https://repo.codemc.io/repository/nms/") + maven("https://repo.rapture.pw/repository/maven-releases/") + maven("https://repo.glaremasters.me/repository/concuncan/") + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven("https://oss.sonatype.org/content/repositories/snapshots/") +} + +dependencies { +// api(platform(project(":gradle:platform"))) +} + +tasks { + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release.set(JAVA_VERSION) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + (options as StandardJavadocDocletOptions) + .tags("apiNote:a:API Note", "implSpec:a:Implementation Requirements", "implNote:a:Implementation Note") + } + + val git = withType { + outputs.upToDateWhen { false } + gitProperties.extProperty = "git" + }.first() + + processResources { + filteringCharset = Charsets.UTF_8.name() + dependsOn(git) + filesMatching(listOf("paper-plugin.yml", "version.txt")) { + expand("gitCommitId" to git.generatedProperties["git.commit.id"]) + } + } +} diff --git a/buildSrc/src/main/kotlin/asp.publishing-conventions.gradle.kts b/buildSrc/src/main/kotlin/asp.publishing-conventions.gradle.kts new file mode 100644 index 000000000..c0ea0d6c5 --- /dev/null +++ b/buildSrc/src/main/kotlin/asp.publishing-conventions.gradle.kts @@ -0,0 +1,86 @@ +import com.infernalsuite.asp.conventions.PublishConfiguration.Companion.publishConfiguration +import org.kordamp.gradle.plugin.profiles.ProfilesExtension + +plugins { + `maven-publish` + signing + id("org.kordamp.gradle.profiles") +} + +val publishConfiguration = publishConfiguration() + +extensions.configure("profiles") { + profile("publish") { + activation { + property { + setKey("publish") + setValue("true") + } + } + action { + extensions.configure("publishing") { + publications { + create("maven") { + groupId = "${project.group}" + artifactId = project.name + version = "${project.version}" + + from(components["java"]) + + pom { + name.set(publishConfiguration.name) + description.set(publishConfiguration.description) + url.set("https://github.com/InfernalSuite/AdvancedSlimePaper") + licenses { + license { + name.set("GNU General Public License, Version 3.0") + url.set("https://www.gnu.org/licenses/gpl-3.0.txt") + } + } + developers { + developer { + id.set("InfernalSuite") + name.set("The InfernalSuite Team") + url.set("https://github.com/InfernalSuite") + email.set("infernalsuite@gmail.com") + } + } + scm { + connection.set("scm:git:https://github.com:InfernalSuite/AdvancedSlimePaper.git") + developerConnection.set("scm:git:ssh://github.com:InfernalSuite/AdvancedSlimePaper.git") + url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/") + } + issueManagement { + system.set("Github") + url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/issues") + } + } + + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + } + } + repositories { + maven { + name = "infernalsuite" + url = uri("https://repo.infernalsuite.com/repository/maven-snapshots/") + credentials { + username = project.property("ISUsername") as String? + password = project.property("ISPassword") as String? + } + } + } + } + extensions.configure("signing") { + useGpgCmd() + sign(extensions.getByName("publishing").publications["maven"]) + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/infernalsuite/asp/conventions/PublishConfiguration.kt b/buildSrc/src/main/kotlin/com/infernalsuite/asp/conventions/PublishConfiguration.kt new file mode 100644 index 000000000..65841170d --- /dev/null +++ b/buildSrc/src/main/kotlin/com/infernalsuite/asp/conventions/PublishConfiguration.kt @@ -0,0 +1,19 @@ +package com.infernalsuite.asp.conventions + +import org.gradle.api.Project +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import javax.inject.Inject + +open class PublishConfiguration @Inject constructor(objects: ObjectFactory) { + + companion object { + internal fun Project.publishConfiguration(): PublishConfiguration { + return extensions.create("publishConfiguration", PublishConfiguration::class.java) + } + } + + val name: Property = objects.property(String::class.java) + val description: Property = objects.property(String::class.java) + +} diff --git a/buildSrc/src/main/kotlin/constants.kt b/buildSrc/src/main/kotlin/constants.kt new file mode 100644 index 000000000..6f6fe73c9 --- /dev/null +++ b/buildSrc/src/main/kotlin/constants.kt @@ -0,0 +1,2 @@ +const val JAVA_VERSION = 21 +const val PAPER_MAVEN_PUBLIC_URL = "https://repo.papermc.io/repository/maven-public/" \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/extensions.kt b/buildSrc/src/main/kotlin/extensions.kt new file mode 100644 index 000000000..2b7913f9d --- /dev/null +++ b/buildSrc/src/main/kotlin/extensions.kt @@ -0,0 +1,24 @@ +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.file.FileSystemLocation +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.the +import java.nio.file.Path + +val Project.libs: LibrariesForLibs + get() = the() + +// Utils for working with java.nio.file.Path from a FileSystemLocation +// Courtesy of PaperMC (io.papermc.paperweight.util/file.kt) <3 +val FileSystemLocation.path: Path + get() = asFile.toPath() + +val Provider.path: Path + get() = get().path + +val Provider.pathOrNull: Path? + get() = orNull?.path + +fun Project.paperApi(): Dependency = + dependencies.create("io.papermc.paper:paper-api:${rootProject.providers.gradleProperty("version").get()}") diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 3a1cad604..a06779a0a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,8 +1,15 @@ plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") } dependencies { compileOnly(project(":api")) - compileOnly(project(":slimeworldmanager-api")) - implementation("com.github.luben:zstd-jni:1.5.2-2") + compileOnly(paperApi()) + implementation(libs.zstd) +} + +publishConfiguration { + name = "Advanced Slime Paper Core" + description = "Core logic for Advanced Slime Paper" } diff --git a/core/src/main/java/com/infernalsuite/aswm/SlimeLogger.java b/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java similarity index 91% rename from core/src/main/java/com/infernalsuite/aswm/SlimeLogger.java rename to core/src/main/java/com/infernalsuite/asp/SlimeLogger.java index 02e6d6826..f71da1ed9 100644 --- a/core/src/main/java/com/infernalsuite/aswm/SlimeLogger.java +++ b/core/src/main/java/com/infernalsuite/asp/SlimeLogger.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm; +package com.infernalsuite.asp; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/core/src/main/java/com/infernalsuite/aswm/Util.java b/core/src/main/java/com/infernalsuite/asp/Util.java similarity index 86% rename from core/src/main/java/com/infernalsuite/aswm/Util.java rename to core/src/main/java/com/infernalsuite/asp/Util.java index 643a5098c..1a3652ffe 100644 --- a/core/src/main/java/com/infernalsuite/aswm/Util.java +++ b/core/src/main/java/com/infernalsuite/asp/Util.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm; +package com.infernalsuite.asp; public final class Util { diff --git a/core/src/main/java/com/infernalsuite/asp/pdc/AdventureDataTypeRegistry.java b/core/src/main/java/com/infernalsuite/asp/pdc/AdventureDataTypeRegistry.java new file mode 100644 index 000000000..fa1622199 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/pdc/AdventureDataTypeRegistry.java @@ -0,0 +1,181 @@ +package com.infernalsuite.asp.pdc; + +import com.google.common.base.Preconditions; +import com.infernalsuite.asp.api.SlimeNMSBridge; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagType; +import net.kyori.adventure.nbt.BinaryTagTypes; +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.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 org.bukkit.persistence.ListPersistentDataType; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class AdventureDataTypeRegistry { + + public static final AdventureDataTypeRegistry DEFAULT = new AdventureDataTypeRegistry(); + private final ConcurrentMap, TagAdapter> adapters = new ConcurrentHashMap<>(); + + @SuppressWarnings("unchecked") + private TagAdapter obtainAdapter(final PersistentDataType dataType) { + return (TagAdapter) this.adapters.computeIfAbsent(dataType.getPrimitiveType(), this::createAdapter); + } + + @SuppressWarnings("unchecked") + private TagAdapter createAdapter(final Class

primitiveType) { + if (TagAdapter.PRIMITIVE_ADAPTERS.containsKey(primitiveType)) { + return (TagAdapter) TagAdapter.PRIMITIVE_ADAPTERS.get(primitiveType); + } else if (PersistentDataContainer.class.isAssignableFrom(primitiveType)) { + return (TagAdapter) TagAdapter.of(PersistentDataContainer.class, CompoundBinaryTag.class, BinaryTagTypes.COMPOUND, this::extractPDC, this::buildPDC); + } else if (primitiveType.isArray() && PersistentDataContainer.class.isAssignableFrom(primitiveType.componentType())) { + return (TagAdapter) TagAdapter.of(PersistentDataContainer[].class, ListBinaryTag.class, BinaryTagTypes.LIST, containers -> { + ListBinaryTag.Builder builder = ListBinaryTag.builder(BinaryTagTypes.COMPOUND); + for (PersistentDataContainer container : containers) { + builder.add(this.extractPDC(container)); + } + return builder.build(); + }, tag -> { + PersistentDataContainer[] containers = new PersistentDataContainer[tag.size()]; + for (int i = 0; i < tag.size(); i++) { + containers[i] = this.buildPDC(tag.getCompound(i)); + } + return containers; + }); + } else if (List.class.isAssignableFrom(primitiveType)) { + return (TagAdapter) TagAdapter.of(List.class, ListBinaryTag.class, BinaryTagTypes.LIST, this::constructList, this::extractList, this::matchesListTag); + } else { + throw new IllegalArgumentException("Could not find a valid TagAdapter implementation for the requested type " + primitiveType.getSimpleName()); + } + } + + private CompoundBinaryTag extractPDC(PersistentDataContainer pdc) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder(); + + if (pdc instanceof AdventurePersistentDataContainer container) { + builder.put(container.getRaw()); + } else { + SlimeNMSBridge.instance().extractCraftPDC(pdc, builder); + } + + return builder.build(); + } + + private PersistentDataContainer buildPDC(CompoundBinaryTag tag) { + return new AdventurePersistentDataContainer(tag, this); + } + + public

BinaryTag wrap(final PersistentDataType dataType, final P value) throws IllegalArgumentException { + return this.obtainAdapter(dataType).build(dataType, value); + } + + public

boolean isInstanceOf(final PersistentDataType dataType, final BinaryTag tag) throws IllegalArgumentException{ + return this.obtainAdapter(dataType).isInstance(dataType, tag); + } + + public P extract(final PersistentDataType dataType, final BinaryTag tag) throws ClassCastException, IllegalArgumentException { + final Class

primitiveType = dataType.getPrimitiveType(); + final TagAdapter adapter = this.obtainAdapter(dataType); + Preconditions.checkArgument(adapter.isInstance(dataType, tag), "The found tag instance (%s) cannot store %s", tag.getClass().getSimpleName(), primitiveType.getSimpleName()); + + final P foundValue = adapter.extract(dataType, tag); + Preconditions.checkArgument(primitiveType.isInstance(foundValue), "The found object is type %s, expected type %s", foundValue.getClass().getSimpleName(), primitiveType.getSimpleName()); + return foundValue; + } + + private > ListBinaryTag constructList(final PersistentDataType dataType, final List

list) { + Preconditions.checkArgument(dataType instanceof ListPersistentDataType, "The passed list cannot be written to the PDC with a %s (expected a list data type", dataType.getClass().getSimpleName()); + @SuppressWarnings("unchecked") final ListPersistentDataType listDataType = (ListPersistentDataType) dataType; + + final ListBinaryTag.Builder builder = ListBinaryTag.builder(); + list.forEach(primitive -> builder.add(this.wrap(listDataType.elementType(), primitive))); + + return builder.build(); + } + + private

List

extractList(final PersistentDataType dataType, final ListBinaryTag listTag) { + Preconditions.checkArgument(dataType instanceof ListPersistentDataType, "The found list tag cannot be read with a %s (expected a list data type)", dataType.getClass().getSimpleName()); + @SuppressWarnings("unchecked") final ListPersistentDataType listDataType = (ListPersistentDataType) dataType; + + return listTag.stream().map(tag -> this.extract(listDataType.elementType(), tag)).collect(Collectors.toList()); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private boolean matchesListTag(final PersistentDataType dataType, final BinaryTag tag) { + if (!(dataType instanceof final ListPersistentDataType listDataType)) return false; + if (!(tag instanceof final ListBinaryTag listTag)) return false; + + final TagAdapter adapter = this.obtainAdapter(listDataType.elementType()); + + return adapter.nbtType().id() == listTag.elementType().id(); + } + + private record TagAdapter( + Class

primitiveType, + Class tagType, + BinaryTagType nbtType, + BiFunction, P, T> builder, + BiFunction, T, P> extractor, + BiPredicate, BinaryTag> matcher + ) { + private static final Map, TagAdapter> PRIMITIVE_ADAPTERS = initPrimitiveAdapters(); + + private static TagAdapter of(Class

primitiveType, Class tagType, BinaryTagType nbtType, Function builder, Function extractor) { + return of(primitiveType, tagType, nbtType, (type, p) -> builder.apply(p), (type, t) -> extractor.apply(t), (type, tag) -> tagType.isInstance(tag)); + } + + private static TagAdapter of(Class

primitiveType, Class tagType, BinaryTagType nbtType, BiFunction, P, T> builder, BiFunction, T, P> extractor, BiPredicate, BinaryTag> matcher) { + return new TagAdapter<>(primitiveType, tagType, nbtType, builder, extractor, matcher); + } + + private P extract(final PersistentDataType dataType, final BinaryTag tag) { + Preconditions.checkArgument(this.tagType.isInstance(tag), "The provided tag was type %s, expected %s", tag.getClass().getSimpleName(), this.tagType.getSimpleName()); + return this.extractor.apply(dataType, this.tagType.cast(tag)); + } + + T build(final PersistentDataType dataType, final P value) { + Preconditions.checkArgument(this.primitiveType.isInstance(value), "The provided value was type %s, expected %s", value.getClass().getSimpleName(), this.primitiveType.getSimpleName()); + return this.builder.apply(dataType, value); + } + + boolean isInstance(final PersistentDataType dataType, final BinaryTag tag) { + return this.matcher.test(dataType, tag); + } + + private static Map, TagAdapter> initPrimitiveAdapters() { + final Map, TagAdapter> adapters = new IdentityHashMap<>(); + adapters.put(Byte.class, TagAdapter.of(Byte.class, ByteBinaryTag.class, BinaryTagTypes.BYTE, ByteBinaryTag::byteBinaryTag, ByteBinaryTag::value)); + adapters.put(Short.class, TagAdapter.of(Short.class, ShortBinaryTag.class, BinaryTagTypes.SHORT, ShortBinaryTag::shortBinaryTag, ShortBinaryTag::value)); + adapters.put(Integer.class, TagAdapter.of(Integer.class, IntBinaryTag.class, BinaryTagTypes.INT, IntBinaryTag::intBinaryTag, IntBinaryTag::value)); + adapters.put(Long.class, TagAdapter.of(Long.class, LongBinaryTag.class, BinaryTagTypes.LONG, LongBinaryTag::longBinaryTag, LongBinaryTag::value)); + adapters.put(Float.class, TagAdapter.of(Float.class, FloatBinaryTag.class, BinaryTagTypes.FLOAT, FloatBinaryTag::floatBinaryTag, FloatBinaryTag::value)); + adapters.put(Double.class, TagAdapter.of(Double.class, DoubleBinaryTag.class, BinaryTagTypes.DOUBLE, DoubleBinaryTag::doubleBinaryTag, DoubleBinaryTag::value)); + adapters.put(String.class, TagAdapter.of(String.class, StringBinaryTag.class, BinaryTagTypes.STRING, StringBinaryTag::stringBinaryTag, StringBinaryTag::value)); + adapters.put(byte[].class, TagAdapter.of(byte[].class, ByteArrayBinaryTag.class, BinaryTagTypes.BYTE_ARRAY, ByteArrayBinaryTag::byteArrayBinaryTag, ByteArrayBinaryTag::value)); + adapters.put(int[].class, TagAdapter.of(int[].class, IntArrayBinaryTag.class, BinaryTagTypes.INT_ARRAY, IntArrayBinaryTag::intArrayBinaryTag, IntArrayBinaryTag::value)); + adapters.put(long[].class, TagAdapter.of(long[].class, LongArrayBinaryTag.class, BinaryTagTypes.LONG_ARRAY, LongArrayBinaryTag::longArrayBinaryTag, LongArrayBinaryTag::value)); + return adapters; + } + + } + +} diff --git a/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java b/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java new file mode 100644 index 000000000..07ec82950 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/pdc/AdventurePersistentDataContainer.java @@ -0,0 +1,188 @@ +package com.infernalsuite.asp.pdc; + +import com.google.common.base.Preconditions; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataAdapterContext; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class AdventurePersistentDataContainer implements PersistentDataContainer, PersistentDataAdapterContext { + + private static final Pattern NS_KEY_PATTERN = Pattern.compile(":"); + + private final ConcurrentMap tags = new ConcurrentHashMap<>(); + private final AdventureDataTypeRegistry registry; + + public AdventurePersistentDataContainer() { + this(AdventureDataTypeRegistry.DEFAULT); + } + + public AdventurePersistentDataContainer(final AdventureDataTypeRegistry registry) { + this.registry = registry; + } + + public AdventurePersistentDataContainer(final Map tags, final AdventureDataTypeRegistry registry) { + this(registry); + this.tags.putAll(tags); + } + + public AdventurePersistentDataContainer(final CompoundBinaryTag root, final AdventureDataTypeRegistry registry) { + this(registry); + root.forEach(entry -> this.tags.put(entry.getKey(), entry.getValue())); + } + + public AdventurePersistentDataContainer(final CompoundBinaryTag root) { + this(root, AdventureDataTypeRegistry.DEFAULT); + } + + public AdventurePersistentDataContainer(final Map tags) { + this(tags, AdventureDataTypeRegistry.DEFAULT); + } + + Map getRaw() { + return this.tags; + } + + public Map getTags() { + return Collections.unmodifiableMap(this.tags); + } + + public CompoundBinaryTag toCompound() { + return CompoundBinaryTag.builder().put(this.tags).build(); + } + + @Override + public void set(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull C value) { + Preconditions.checkNotNull(key, "The key cannot be null"); + Preconditions.checkNotNull(type, "The provided type cannot be null"); + Preconditions.checkNotNull(value, "The provided value cannot be null"); + this.tags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, getAdapterContext()))); + } + + @Override + public boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { + Preconditions.checkNotNull(key, "The key cannot be null"); + Preconditions.checkNotNull(type, "The provided type cannot be null"); + + BinaryTag tag = this.tags.get(key.toString()); + + return tag != null && this.registry.isInstanceOf(type, tag); + } + + @Override + public boolean has(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "The key cannot be null"); + return this.tags.containsKey(key.toString()); + } + + @Override + public @Nullable C get(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { + Preconditions.checkNotNull(key, "The key cannot be null"); + Preconditions.checkNotNull(type, "The provided type cannot be null"); + + final BinaryTag tag = this.tags.get(key.toString()); + + return tag == null ? null : type.fromPrimitive(this.registry.extract(type, tag), getAdapterContext()); + } + + @Override + public @NotNull C getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull C defaultValue) { + final C value = this.get(key, type); + return value == null ? defaultValue : value; + } + + @Override + public @NotNull Set getKeys() { + return this.tags.keySet().stream() + .map(key -> NS_KEY_PATTERN.split(key, 2)) + .filter(keyData -> keyData.length == 2) + .map(keyData -> new NamespacedKey(keyData[0], keyData[1])) + .collect(Collectors.toUnmodifiableSet()); + } + + @Override + public void remove(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "The key cannot be null"); + this.tags.remove(key.toString()); + } + + @Override + public boolean isEmpty() { + return this.tags.isEmpty(); + } + + @Override + public void copyTo(@NotNull PersistentDataContainer other, boolean replace) { + Preconditions.checkNotNull(other, "The provided container cannot be null"); + + if (other instanceof AdventurePersistentDataContainer container) { + if (replace) { + container.tags.putAll(this.tags); + } else { + this.tags.forEach(container.tags::putIfAbsent); + } + } else { + throw new IllegalArgumentException("Cannot copy to a container that isn't an AdventurePersistentDataContainer (got " + other.getClass().getName() + ")"); + } + } + + @Override + public @NotNull PersistentDataAdapterContext getAdapterContext() { + return this; + } + + @Override + public byte @NotNull [] serializeToBytes() throws IOException { + if (this.tags.isEmpty()) return new byte[0]; + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BinaryTagIO.writer().write(CompoundBinaryTag.builder().put(this.tags).build(), outputStream); + + return outputStream.toByteArray(); + } + + @Override + public void readFromBytes(byte @NotNull [] bytes, boolean clear) throws IOException { + if (clear) this.tags.clear(); + if (bytes.length == 0) return; + + BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(bytes)).forEach(entry -> this.tags.put(entry.getKey(), entry.getValue())); + } + + @Override + public @NotNull PersistentDataContainer newPersistentDataContainer() { + return new AdventurePersistentDataContainer(this.registry); + } + + @Override + public int hashCode() { + int hashCode = 3; + hashCode =+ this.tags.hashCode(); + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + AdventurePersistentDataContainer other = (AdventurePersistentDataContainer) obj; + return this.tags.equals(other.tags); + } +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/SlimeSerializationAdapterImpl.java b/core/src/main/java/com/infernalsuite/asp/serialization/SlimeSerializationAdapterImpl.java new file mode 100644 index 000000000..451bcdaff --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/SlimeSerializationAdapterImpl.java @@ -0,0 +1,40 @@ +package com.infernalsuite.asp.serialization; + +import com.infernalsuite.asp.api.SlimeNMSBridge; +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.loaders.SlimeSerializationAdapter; +import com.infernalsuite.asp.api.utils.SlimeFormat; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.SlimeWorldInstance; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.serialization.slime.SlimeSerializer; +import com.infernalsuite.asp.serialization.slime.reader.SlimeWorldReaderRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public class SlimeSerializationAdapterImpl implements SlimeSerializationAdapter { + + @Override + public byte[] serializeWorld(@NotNull SlimeWorld slimeWorld) { + if(slimeWorld instanceof SlimeWorldInstance) { + throw new IllegalArgumentException("SlimeWorldInstances cannot be serialized directly. Use SlimeWorldInstance.getSerializableCopy() instead."); + } + return SlimeSerializer.serialize(slimeWorld); + } + + @Override + public @NotNull SlimeWorld deserializeWorld(@NotNull String worldName, byte[] serializedWorld, @Nullable SlimeLoader loader, @NotNull SlimePropertyMap propertyMap, boolean readOnly) throws CorruptedWorldException, NewerFormatException, IOException { + SlimeWorld slimeWorld = SlimeWorldReaderRegistry.readWorld(loader, worldName, serializedWorld, propertyMap, loader == null || readOnly); + return SlimeNMSBridge.instance().getSlimeDataConverter().applyDataFixers(slimeWorld); + } + + @Override + public int getSlimeFormat() { + return SlimeFormat.SLIME_VERSION; + } + +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/SlimeWorldReader.java b/core/src/main/java/com/infernalsuite/asp/serialization/SlimeWorldReader.java new file mode 100644 index 000000000..a6e166cac --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/SlimeWorldReader.java @@ -0,0 +1,8 @@ +package com.infernalsuite.asp.serialization; + +import com.infernalsuite.asp.api.world.SlimeWorld; + +public interface SlimeWorldReader { + + SlimeWorld readFromData(T data); +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilImportData.java b/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilImportData.java new file mode 100644 index 000000000..bf8c4eb47 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilImportData.java @@ -0,0 +1,15 @@ +package com.infernalsuite.asp.serialization.anvil; + +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.nio.file.Path; + +public record AnvilImportData(Path worldDir, String newName, @Nullable SlimeLoader loader) { + + public static AnvilImportData legacy(File worldDir, String newName, @Nullable SlimeLoader loader) { + return new AnvilImportData(worldDir.toPath(), newName, loader); + } + +} 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 new file mode 100644 index 000000000..7a9820249 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/anvil/AnvilWorldReader.java @@ -0,0 +1,345 @@ +package com.infernalsuite.asp.serialization.anvil; + +import com.infernalsuite.asp.Util; +import com.infernalsuite.asp.api.exceptions.InvalidWorldException; +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.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.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.IntBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +public class AnvilWorldReader implements com.infernalsuite.asp.serialization.SlimeWorldReader { + + private static final int SECTOR_SIZE = 4096; + + private static final Logger LOGGER = LoggerFactory.getLogger(AnvilWorldReader.class); + + public static final AnvilWorldReader INSTANCE = new AnvilWorldReader(); + + @Override + public SlimeWorld readFromData(AnvilImportData importData) { + Path worldDir = importData.worldDir(); + + try { + Path levelFile = worldDir.resolve("level.dat"); + if (!Files.exists(levelFile) || !Files.isRegularFile(levelFile)) { + throw new RuntimeException(new InvalidWorldException(worldDir)); + } + + LevelData data = readLevelData(levelFile); + + int worldVersion = data.version; + + SlimePropertyMap propertyMap = new SlimePropertyMap(); + + // TODO - Really? There has to be a better way... + Path environmentDir = worldDir.resolve("DIM-1"); + propertyMap.setValue(SlimeProperties.ENVIRONMENT, "nether"); + if (!Files.isDirectory(environmentDir)) { + environmentDir = worldDir.resolve("DIM1"); + propertyMap.setValue(SlimeProperties.ENVIRONMENT, "the_end"); + if (!Files.isDirectory(environmentDir)) { + environmentDir = worldDir; + propertyMap.setValue(SlimeProperties.ENVIRONMENT, "normal"); + } + } + + // Chunks + Path regionDir = environmentDir.resolve("region"); + + if (!Files.exists(regionDir) || !Files.isDirectory(regionDir)) { + throw new InvalidWorldException(environmentDir); + } + + Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); + + try (var stream = Files.newDirectoryStream(regionDir, path -> path.toString().endsWith(".mca"))) { + for (final Path path : stream) { + LOGGER.info("Loading region file {}...", path.getFileName()); + chunks.putAll(loadChunks(path, worldVersion).stream() + .collect(Collectors.toMap(chunk -> Util.chunkPosition(chunk.getX(), chunk.getZ()), Function.identity()))); + } + } + + // Entity serialization + Path entityDir = environmentDir.resolve("entities"); + if (Files.exists(entityDir)) { + if (!Files.isDirectory(entityDir)) throw new InvalidWorldException(environmentDir, "'entities' is not a directory!"); + + try (var stream = Files.newDirectoryStream(entityDir, path -> path.toString().endsWith(".mca"))) { + for (final Path path : stream) { + LOGGER.info("Loading entity region file {}...", path.getFileName()); + loadEntities(path, worldVersion, chunks); + } + } + } + + + if (chunks.isEmpty()) { + 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); + + return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(importData.newName(), importData.loader(), importData.loader() == null, + chunks, new ConcurrentHashMap<>(), propertyMap, worldVersion); + } catch (IOException | InvalidWorldException e) { + + throw new RuntimeException(e); + } + } + + 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; + + tag = BinaryTagIO.unlimitedReader().read(file, BinaryTagIO.Compression.GZIP); + + CompoundBinaryTag dataTag = tag.getCompound("Data"); + if (dataTag.size() != 0) { + int dataVersion = dataTag.getInt("DataVersion", -1); + int spawnX = dataTag.getInt("SpawnX", 0); + int spawnY = dataTag.getInt("SpawnY", 255); + int spawnZ = dataTag.getInt("SpawnZ", 0); + return new LevelData(dataVersion, spawnX, spawnY, spawnZ); + } + + throw new InvalidWorldException(file.getParent()); + } + + private static void loadEntities(Path path, int version, Long2ObjectMap chunkMap) throws IOException { + byte[] regionByteArray = Files.readAllBytes(path); + DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(regionByteArray)); + + List chunks = new ArrayList<>(1024); + + for (int i = 0; i < 1024; i++) { + int entry = inputStream.readInt(); + int chunkOffset = entry >>> 8; + int chunkSize = entry & 15; + + if (entry != 0) { + ChunkEntry chunkEntry = new ChunkEntry(chunkOffset * SECTOR_SIZE, chunkSize * SECTOR_SIZE); + chunks.add(chunkEntry); + } + } + + for (ChunkEntry entry : chunks) { + try { + DataInputStream headerStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset(), entry.paddedSize())); + + int chunkSize = headerStream.readInt() - 1; + int compressionScheme = headerStream.readByte(); + + DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset() + 5, chunkSize)); + InputStream decompressorStream = compressionScheme == 1 ? new GZIPInputStream(chunkStream) : new InflaterInputStream(chunkStream); + CompoundBinaryTag tag = BinaryTagIO.unlimitedReader().read(decompressorStream); + + readEntityChunk(tag, version, chunkMap); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + } + + private static List loadChunks(Path path, int worldVersion) throws IOException { + byte[] regionByteArray = Files.readAllBytes(path); + DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(regionByteArray)); + + List chunks = new ArrayList<>(1024); + + for (int i = 0; i < 1024; i++) { + int entry = inputStream.readInt(); + int chunkOffset = entry >>> 8; + int chunkSize = entry & 15; + + if (entry != 0) { + ChunkEntry chunkEntry = new ChunkEntry(chunkOffset * SECTOR_SIZE, chunkSize * SECTOR_SIZE); + chunks.add(chunkEntry); + } + } + + return chunks.stream().map((entry) -> { + + try { + DataInputStream headerStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset(), entry.paddedSize())); + + int chunkSize = headerStream.readInt() - 1; + int compressionScheme = headerStream.readByte(); + + DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset() + 5, chunkSize)); + InputStream decompressorStream = compressionScheme == 1 ? new GZIPInputStream(chunkStream) : new InflaterInputStream(chunkStream); + CompoundBinaryTag tag = BinaryTagIO.unlimitedReader().read(decompressorStream); + return readChunk(tag, worldVersion); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + private static void readEntityChunk(CompoundBinaryTag compound, int worldVersion, Long2ObjectMap slimeChunkMap) { + int[] position = compound.getIntArray("Position"); + if (position.length == 0) throw new IllegalStateException("Entity chunk is missing position data"); + int chunkX = position[0]; + int chunkZ = position[1]; + + int dataVersion = compound.getInt("DataVersion", -1); + if (dataVersion != worldVersion) { + LOGGER.error("Cannot load entity chunk at {},{}: data version {} does not match world version {}", chunkX, chunkZ, dataVersion, worldVersion); + return; + } + + SlimeChunk chunk = slimeChunkMap.get(Util.chunkPosition(chunkX, chunkZ)); + if (chunk == null) { + LOGGER.warn("Lost entity chunk data at: {},{}", chunkX, chunkZ); + } else { + List entities = new ArrayList<>(chunk.getEntities()); + for (BinaryTag binaryTag : compound.getList("Entities", BinaryTagTypes.COMPOUND)) { + entities.add((CompoundBinaryTag) binaryTag); + } + + slimeChunkMap.put(Util.chunkPosition(chunkX, chunkZ), new SlimeChunkSkeleton( + chunk.getX(), + chunk.getZ(), + chunk.getSections(), + chunk.getHeightMaps(), + chunk.getTileEntities(), + entities, + chunk.getExtraData(), + chunk.getUpgradeData() + )); + } + } + + private static SlimeChunk readChunk(CompoundBinaryTag compound, int worldVersion) { + int chunkX = compound.getInt("xPos"); + int chunkZ = compound.getInt("zPos"); + + int dataVersion = compound.getInt("DataVersion", -1); + if (dataVersion != worldVersion) { + LOGGER.error("Cannot load chunk at {},{}: data version {} does not match world version {}", chunkX, chunkZ, dataVersion, worldVersion); + return null; + } + + String status = compound.getString("Status", ""); + if (!status.isEmpty()) { + // TODO - Check if this is correct, looks like the status string format may have changed... + if (!status.equals("postprocessed") && !status.startsWith("full") && !status.startsWith("minecraft:full")) { + // It's a protochunk + return null; + } + } + + CompoundBinaryTag heightMaps = compound.getCompound("Heightmaps"); + + List tileEntities = compound.getList("block_entities", BinaryTagTypes.COMPOUND).stream().map(t -> (CompoundBinaryTag) t).toList(); + List entities = compound.getList("entities", BinaryTagTypes.COMPOUND).stream().map(t -> (CompoundBinaryTag) t).toList(); + ListBinaryTag sectionsTag = compound.getList("sections", BinaryTagTypes.COMPOUND); + + int minSectionY = compound.getInt("yPos"); + // TODO - look into this +1 below + int maxSectionY = sectionsTag.stream().map(tag -> ((CompoundBinaryTag) tag).getByte("Y")).max(Byte::compareTo).orElse((byte) 0) + 1; // Add 1 to the section, as we serialize it with the 1 added. + + SlimeChunkSection[] sectionArray = new SlimeChunkSection[maxSectionY - minSectionY]; + + for (final BinaryTag rawRag : sectionsTag) { + final CompoundBinaryTag sectionTag = (CompoundBinaryTag) rawRag; + int index = sectionTag.getByte("Y"); + + CompoundBinaryTag blockStatesTag = sectionTag.getCompound("block_states"); + CompoundBinaryTag biomesTag = sectionTag.getCompound("biomes"); + + // TODO - actually, the section is empty if the block_states palette only contains air so uh... yeah xD fix this :P + // NB - maybe consider an import flag to respect the original biome even if its an empty section, or just strip and replace with the world default + if (blockStatesTag.size() == 0 && biomesTag.size() == 0) continue; // Empty section + + NibbleArray blockLightArray = applyByteArrayOrNull(sectionTag, "BlockLight", NibbleArray::new); + NibbleArray skyLightArray = applyByteArrayOrNull(sectionTag, "SkyLight", NibbleArray::new); + + sectionArray[index - minSectionY] = new SlimeChunkSectionSkeleton(blockStatesTag, biomesTag, blockLightArray, skyLightArray); + } + + Map extraTag = new HashMap<>(); + CompoundBinaryTag chunkBukkitValues = compound.getCompound("ChunkBukkitValues"); + if (!chunkBukkitValues.isEmpty()) extraTag.put("ChunkBukkitValues", chunkBukkitValues); + + // Find first non-null chunk section. If all sections are null, chunk is empty so return null + return Arrays.stream(sectionArray) + .filter(Objects::nonNull) + .findFirst() + .map(x -> new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMaps, tileEntities, entities, extraTag, null)) + .orElse(null); + } + + private static T applyByteArrayOrNull(final CompoundBinaryTag tag, final String key, final Function transform) { + byte[] res = tag.getByteArray(key); + return res.length == 0 ? null : transform.apply(res); + } + + private record ChunkEntry(int offset, int paddedSize) {} + + private record LevelData(int version, int x, int y, int z) {} + +} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/ChunkPruner.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/ChunkPruner.java similarity index 59% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/ChunkPruner.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/ChunkPruner.java index 389c6f6e5..892a327bd 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/ChunkPruner.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/ChunkPruner.java @@ -1,12 +1,13 @@ -package com.infernalsuite.aswm.serialization.slime; +package com.infernalsuite.asp.serialization.slime; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.ListTag; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +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 net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import java.util.List; @@ -50,22 +51,20 @@ public static boolean canBePruned(SlimeWorld world, SlimeChunk chunk) { private static boolean areSectionsEmpty(SlimeChunkSection[] sections) { for (SlimeChunkSection chunkSection : sections) { try { - List palettes = chunkSection.getBlockStatesTag().getAsListTag("palette") - .get().getAsCompoundTagList() - .get().getValue(); - - if (palettes.size() > 1) return false; // If there is more than one palette, the section is not empty - if (!palettes.get(0).getStringValue("Name").get().equals("minecraft:air")) { - return false; + ListBinaryTag paletteTag = chunkSection.getBlockStatesTag().getList("palette"); + if (paletteTag.elementType() != BinaryTagTypes.COMPOUND) { + continue; // If the element type isn't a compound tag, consider the section empty } - } catch (Exception e) { + List palette = paletteTag.stream().map(tag -> (CompoundBinaryTag) tag).toList(); + if (palette.size() > 1) return false; // If there is more than one palette, the section is not empty + if (!palette.getFirst().getString("Name").equals("minecraft:air")) return false; // If the only palette entry is not air, the section is not empty + } catch (final Exception e) { return false; } - // The section is empty, continue to the next one } - // All sections are empty, we can omit this chunk return true; } + } diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/SlimeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java similarity index 62% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/SlimeSerializer.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java index b2ed62c08..7252a6400 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/SlimeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/SlimeSerializer.java @@ -1,39 +1,41 @@ -package com.infernalsuite.aswm.serialization.slime; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.TagType; -import com.flowpowered.nbt.stream.NBTInputStream; -import com.flowpowered.nbt.stream.NBTOutputStream; +package com.infernalsuite.asp.serialization.slime; + import com.github.luben.zstd.Zstd; -import com.infernalsuite.aswm.api.utils.SlimeFormat; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.api.utils.SlimeFormat; +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.SlimePropertyMap; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.nio.ByteOrder; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; public class SlimeSerializer { private static final Logger LOGGER = LoggerFactory.getLogger(SlimeSerializer.class); public static byte[] serialize(SlimeWorld world) { - CompoundTag extraData = world.getExtraData(); + Map extraData = world.getExtraData(); SlimePropertyMap propertyMap = world.getPropertyMap(); // Store world properties - if (!extraData.getValue().containsKey("properties")) { - extraData.getValue().putIfAbsent("properties", propertyMap.toCompound()); + if (!extraData.containsKey("properties")) { + extraData.putIfAbsent("properties", propertyMap.toCompound()); } else { - extraData.getValue().replace("properties", propertyMap.toCompound()); + extraData.replace("properties", propertyMap.toCompound()); } ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); @@ -56,20 +58,17 @@ public static byte[] serialize(SlimeWorld world) { outStream.write(compressedChunkData); // Extra Tag - { - byte[] extra = serializeCompoundTag(extraData); - byte[] compressedExtra = Zstd.compress(extra); + byte[] extra = serializeCompoundTag(CompoundBinaryTag.builder().put(extraData).build()); + byte[] compressedExtra = Zstd.compress(extra); - outStream.writeInt(compressedExtra.length); - outStream.writeInt(extra.length); - outStream.write(compressedExtra); - } + outStream.writeInt(compressedExtra.length); + outStream.writeInt(extra.length); + outStream.write(compressedExtra); } catch (Exception e) { throw new RuntimeException(e); } - return outByteStream.toByteArray(); } @@ -124,45 +123,46 @@ static byte[] serializeChunks(SlimeWorld world, Collection chunks) t outStream.write(heightMaps); // Tile entities - ListTag tileEntitiesNbtList = new ListTag<>("tileEntities", TagType.TAG_COMPOUND, chunk.getTileEntities()); - CompoundTag tileEntitiesCompound = new CompoundTag("", new CompoundMap(Collections.singletonList(tileEntitiesNbtList))); + ListBinaryTag tileEntitiesNbtList = ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, yayGenerics(chunk.getTileEntities())); + CompoundBinaryTag tileEntitiesCompound = CompoundBinaryTag.builder().put("tileEntities", tileEntitiesNbtList).build(); byte[] tileEntitiesData = serializeCompoundTag(tileEntitiesCompound); outStream.writeInt(tileEntitiesData.length); outStream.write(tileEntitiesData); // Entities - ListTag entitiesNbtList = new ListTag<>("entities", TagType.TAG_COMPOUND, chunk.getEntities()); - CompoundTag entitiesCompound = new CompoundTag("", new CompoundMap(Collections.singletonList(entitiesNbtList))); + ListBinaryTag entitiesNbtList = ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, yayGenerics(chunk.getEntities())); + CompoundBinaryTag entitiesCompound = CompoundBinaryTag.builder().put("entities", entitiesNbtList).build(); byte[] entitiesData = serializeCompoundTag(entitiesCompound); outStream.writeInt(entitiesData.length); outStream.write(entitiesData); // Extra Tag - { - if (chunk.getExtraData() == null) { - LOGGER.warn("Chunk at " + chunk.getX() + ", " + chunk.getZ() + " from world " + world.getName() + " has no extra data! When deserialized, this chunk will have an empty extra data tag!"); - } - byte[] extra = serializeCompoundTag(chunk.getExtraData()); - - outStream.writeInt(extra.length); - outStream.write(extra); + if (chunk.getExtraData() == null) { + LOGGER.warn("Chunk at " + chunk.getX() + ", " + chunk.getZ() + " from world " + world.getName() + " has no extra data! When deserialized, this chunk will have an empty extra data tag!"); } + byte[] extra = serializeCompoundTag(CompoundBinaryTag.from(chunk.getExtraData())); + + outStream.writeInt(extra.length); + outStream.write(extra); } return outByteStream.toByteArray(); } - protected static byte[] serializeCompoundTag(CompoundTag tag) throws IOException { - if (tag == null || tag.getValue().isEmpty()) { - return new byte[0]; - } + protected static byte[] serializeCompoundTag(CompoundBinaryTag tag) throws IOException { + if (tag == null || tag.size() == 0) return new byte[0]; + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); - NBTOutputStream outStream = new NBTOutputStream(outByteStream, NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - outStream.writeTag(tag); + BinaryTagIO.writer().write(tag, outByteStream); return outByteStream.toByteArray(); } + @SuppressWarnings("unchecked") + private static List yayGenerics(final List tags) { + return (List) tags; + } + } diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/SlimeWorldReaderRegistry.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java similarity index 69% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/SlimeWorldReaderRegistry.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java index b0d734e81..57ff7f257 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/SlimeWorldReaderRegistry.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/SlimeWorldReaderRegistry.java @@ -1,15 +1,15 @@ -package com.infernalsuite.aswm.serialization.slime.reader; - -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.utils.SlimeFormat; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v11.v11WorldFormat; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v12.v12WorldFormat; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9WorldFormat; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v10.v10WorldFormat; +package com.infernalsuite.asp.serialization.slime.reader; + +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.SlimeFormat; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +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.v1_9.v1_9WorldFormat; import java.io.ByteArrayInputStream; import java.io.DataInputStream; diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/VersionedByteSlimeWorldReader.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/VersionedByteSlimeWorldReader.java similarity index 54% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/VersionedByteSlimeWorldReader.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/VersionedByteSlimeWorldReader.java index e6507f019..4c1d95357 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/VersionedByteSlimeWorldReader.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/VersionedByteSlimeWorldReader.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.serialization.slime.reader; +package com.infernalsuite.asp.serialization.slime.reader; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +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.world.properties.SlimePropertyMap; import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/SimpleWorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/SimpleWorldFormat.java similarity index 51% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/SimpleWorldFormat.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/SimpleWorldFormat.java index 4069f0dfa..8d4c60dc8 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/SimpleWorldFormat.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/SimpleWorldFormat.java @@ -1,12 +1,11 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl; +package com.infernalsuite.asp.serialization.slime.reader.impl; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.serialization.SlimeWorldReader; -import com.infernalsuite.aswm.serialization.slime.reader.VersionedByteSlimeWorldReader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +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.serialization.slime.reader.VersionedByteSlimeWorldReader; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; @@ -14,10 +13,10 @@ public class SimpleWorldFormat implements VersionedByteSlimeWorldReader { - private final SlimeWorldReader data; + private final com.infernalsuite.asp.serialization.SlimeWorldReader data; private final VersionedByteSlimeWorldReader reader; - public SimpleWorldFormat(SlimeWorldReader data, VersionedByteSlimeWorldReader reader) { + public SimpleWorldFormat(com.infernalsuite.asp.serialization.SlimeWorldReader data, VersionedByteSlimeWorldReader reader) { this.data = data; this.reader = reader; } diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java similarity index 57% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java index 8ece7660e..e83dbb544 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10SlimeWorldDeSerializer.java @@ -1,38 +1,30 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v10; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.DoubleTag; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.stream.NBTInputStream; -import com.github.luben.zstd.Zstd; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.serialization.slime.reader.VersionedByteSlimeWorldReader; -import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +package com.infernalsuite.asp.serialization.slime.reader.impl.v10; +import com.github.luben.zstd.Zstd; +import com.infernalsuite.asp.Util; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader; +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.SlimeChunkSectionSkeleton; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.nbt.*; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; class v10SlimeWorldDeSerializer implements VersionedByteSlimeWorldReader { @@ -56,14 +48,15 @@ public SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worl byte[] extra = readCompressed(dataStream); // Entity deserialization - com.flowpowered.nbt.CompoundTag entitiesCompound = readCompound(entities); - { - List serializedEntities = ((ListTag) entitiesCompound.getValue().get("entities")).getValue(); - for (CompoundTag entityCompound : serializedEntities) { - ListTag listTag = (ListTag) entityCompound.getAsListTag("Pos").get(); - - int chunkX = listTag.getValue().get(0).getValue().intValue() >> 4; - int chunkZ = listTag.getValue().get(2).getValue().intValue() >> 4; + CompoundBinaryTag entitiesCompound = readCompound(entities); + if(entitiesCompound != null) { + for (BinaryTag binaryTag : entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND)) { + CompoundBinaryTag entityCompound = (CompoundBinaryTag) binaryTag; + + ListBinaryTag listTag = entityCompound.getList("Pos", BinaryTagTypes.DOUBLE); + + int chunkX = ((int) listTag.getDouble(0)) >> 4; + int chunkZ = ((int) listTag.getDouble(2)) >> 4; long chunkKey = Util.chunkPosition(chunkX, chunkZ); SlimeChunk chunk = chunks.get(chunkKey); if (chunk != null) { @@ -73,36 +66,46 @@ public SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worl } // Tile Entity deserialization - com.flowpowered.nbt.CompoundTag tileEntitiesCompound = readCompound(tileEntities); - for (CompoundTag tileEntityCompound : ((com.flowpowered.nbt.ListTag) tileEntitiesCompound.getValue().get("tiles")).getValue()) { - int chunkX = ((IntTag) tileEntityCompound.getValue().get("x")).getValue() >> 4; - int chunkZ = ((IntTag) tileEntityCompound.getValue().get("z")).getValue() >> 4; - long pos = Util.chunkPosition(chunkX, chunkZ); - SlimeChunk chunk = chunks.get(pos); - - if (chunk == null) { - throw new CorruptedWorldException(worldName); - } + CompoundBinaryTag tileEntitiesCompound = readCompound(tileEntities); + if(tileEntitiesCompound != null) { + for (BinaryTag binaryTag : (tileEntitiesCompound.getList("tiles", BinaryTagTypes.COMPOUND))) { + CompoundBinaryTag tileEntityCompound = (CompoundBinaryTag) binaryTag; + + int chunkX = tileEntityCompound.getInt("x") >> 4; + int chunkZ = tileEntityCompound.getInt("z") >> 4; + long pos = Util.chunkPosition(chunkX, chunkZ); + SlimeChunk chunk = chunks.get(pos); + + if (chunk == null) { + throw new CorruptedWorldException(worldName); + } - chunk.getTileEntities().add(tileEntityCompound); + chunk.getTileEntities().add(tileEntityCompound); + } } // Extra Data - com.flowpowered.nbt.CompoundTag extraCompound = readCompound(extra); + CompoundBinaryTag extraCompound = readCompound(extra); // World properties SlimePropertyMap worldPropertyMap = propertyMap; - Optional propertiesMap = extraCompound - .getAsCompoundTag("properties") - .map(com.flowpowered.nbt.CompoundTag::getValue); + CompoundBinaryTag propertiesMap = extraCompound != null && extraCompound.get("properties") != null + ? extraCompound.getCompound("properties") + : null; + + if (propertiesMap != null) { + Map wpm = new HashMap<>(); + propertiesMap.forEach(entry -> wpm.put(entry.getKey(), entry.getValue())); - if (propertiesMap.isPresent()) { - worldPropertyMap = new SlimePropertyMap(propertiesMap.get()); + worldPropertyMap = new SlimePropertyMap(wpm); worldPropertyMap.merge(propertyMap); // Override world properties } - return new SkeletonSlimeWorld(worldName, loader, readOnly, chunks, - extraCompound, + ConcurrentMap extraData = new ConcurrentHashMap<>(); + if (extraCompound != null) extraCompound.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + + return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, + extraData, worldPropertyMap, worldVersion ); @@ -121,7 +124,7 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope // Height Maps byte[] heightMapData = new byte[chunkData.readInt()]; chunkData.read(heightMapData); - com.flowpowered.nbt.CompoundTag heightMaps = readCompound(heightMapData); + CompoundBinaryTag heightMaps = readCompound(heightMapData); // Chunk Sections { @@ -154,22 +157,23 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope // Block data byte[] blockStateData = new byte[chunkData.readInt()]; chunkData.read(blockStateData); - com.flowpowered.nbt.CompoundTag blockStateTag = readCompound(blockStateData); + CompoundBinaryTag blockStateTag = readCompound(blockStateData); // Biome Data byte[] biomeData = new byte[chunkData.readInt()]; chunkData.read(biomeData); - com.flowpowered.nbt.CompoundTag biomeTag = readCompound(biomeData); + CompoundBinaryTag biomeTag = readCompound(biomeData); chunkSectionArray[sectionId] = new SlimeChunkSectionSkeleton( blockStateTag, biomeTag, blockLightArray, - skyLightArray); + skyLightArray + ); } chunkMap.put(Util.chunkPosition(x, z), - new SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>(), new CompoundTag("", new CompoundMap()), null) + new com.infernalsuite.asp.skeleton.SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>(), new HashMap<>(), null) ); } } @@ -197,14 +201,10 @@ private static byte[] readCompressed(DataInputStream stream) throws IOException return normal; } - private static com.flowpowered.nbt.CompoundTag readCompound(byte[] bytes) throws IOException { - if (bytes.length == 0) { - return null; - } + private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { + if (tagBytes.length == 0) return null; - NBTInputStream stream = new NBTInputStream(new ByteArrayInputStream(bytes), NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - return (com.flowpowered.nbt.CompoundTag) stream.readTag(); + return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); } - } \ No newline at end of file diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10WorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10WorldFormat.java new file mode 100644 index 000000000..033377a17 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v10/v10WorldFormat.java @@ -0,0 +1,8 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v10; + +public interface v10WorldFormat { + + // Latest, returns same + com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(data -> data, new v10SlimeWorldDeSerializer()); + +} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java similarity index 55% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java index 0cc8bb099..b4a99a1c6 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11SlimeWorldDeSerializer.java @@ -1,38 +1,32 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v11; +package com.infernalsuite.asp.serialization.slime.reader.impl.v11; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.stream.NBTInputStream; import com.github.luben.zstd.Zstd; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.serialization.slime.reader.VersionedByteSlimeWorldReader; -import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; +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 it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.nbt.*; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; -import java.nio.ByteOrder; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -public class v11SlimeWorldDeSerializer implements VersionedByteSlimeWorldReader { +public class v11SlimeWorldDeSerializer implements com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader { public static final int ARRAY_SIZE = 16 * 16 * 16 / (8 / 4); @@ -44,19 +38,25 @@ public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, S Long2ObjectMap chunks = readChunks(propertyMap, chunkBytes); byte[] extraTagBytes = readCompressed(dataStream); - CompoundTag extraTag = readCompound(extraTagBytes); + CompoundBinaryTag extraTag = readCompound(extraTagBytes); SlimePropertyMap worldPropertyMap = propertyMap; - Optional propertiesMap = extraTag - .getAsCompoundTag("properties") - .map(CompoundTag::getValue); + CompoundBinaryTag propertiesMap = extraTag != null && extraTag.get("properties") != null + ? extraTag.getCompound("properties") + : null; - if (propertiesMap.isPresent()) { - worldPropertyMap = new SlimePropertyMap(propertiesMap.get()); - worldPropertyMap.merge(propertyMap); + if (propertiesMap != null) { + Map wpm = new HashMap<>(); + propertiesMap.forEach(entry -> wpm.put(entry.getKey(), entry.getValue())); + + worldPropertyMap = new SlimePropertyMap(wpm); + worldPropertyMap.merge(propertyMap); // Override world properties } - return new SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraTag, worldPropertyMap, worldVersion); + ConcurrentMap extraData = new ConcurrentHashMap<>(); + if (extraTag != null) extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + + return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraData, worldPropertyMap, worldVersion); } private static Long2ObjectMap readChunks(SlimePropertyMap slimePropertyMap, byte[] chunkBytes) throws IOException { @@ -99,20 +99,20 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope // Block Data byte[] blockStateData = new byte[chunkData.readInt()]; chunkData.read(blockStateData); - CompoundTag blockStateTag = readCompound(blockStateData); + CompoundBinaryTag blockStateTag = readCompound(blockStateData); // Biome Data byte[] biomeData = new byte[chunkData.readInt()]; chunkData.read(biomeData); - CompoundTag biomeTag = readCompound(biomeData); + CompoundBinaryTag biomeTag = readCompound(biomeData); - chunkSections[sectionId] = new SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); + chunkSections[sectionId] = new com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); } // HeightMaps byte[] heightMapData = new byte[chunkData.readInt()]; chunkData.read(heightMapData); - CompoundTag heightMaps = readCompound(heightMapData); + CompoundBinaryTag heightMaps = readCompound(heightMapData); // Tile Entities @@ -123,9 +123,13 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope chunkData.read(compressedTileEntitiesData); Zstd.decompress(decompressedTileEntitiesData, compressedTileEntitiesData); - CompoundTag tileEntitiesCompound = readCompound(decompressedTileEntitiesData); - @SuppressWarnings("unchecked") - List serializedTileEntities = ((ListTag) tileEntitiesCompound.getValue().get("tileEntities")).getValue(); + CompoundBinaryTag tileEntitiesCompound = readCompoundOrEmpty(decompressedTileEntitiesData); + + ListBinaryTag tileEntitiesTag = tileEntitiesCompound.getList("tileEntities", BinaryTagTypes.COMPOUND); + List serializedTileEntities = new ArrayList<>(tileEntitiesTag.size()); + for (BinaryTag binaryTag : tileEntitiesTag) { + serializedTileEntities.add((CompoundBinaryTag) binaryTag); + } // Entities @@ -136,12 +140,15 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope chunkData.read(compressedEntitiesData); Zstd.decompress(decompressedEntitiesData, compressedEntitiesData); - CompoundTag entitiesCompound = readCompound(decompressedEntitiesData); - @SuppressWarnings("unchecked") - List serializedEntities = ((ListTag) entitiesCompound.getValue().get("entities")).getValue(); - + CompoundBinaryTag entitiesCompound = readCompoundOrEmpty(decompressedEntitiesData); + ListBinaryTag entitiesTag = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND); + List serializedEntities = new ArrayList<>(entitiesTag.size()); + for (BinaryTag binaryTag : entitiesTag) { + serializedEntities.add((CompoundBinaryTag) binaryTag); + } + chunkMap.put(Util.chunkPosition(x, z), - new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, new CompoundTag("", new CompoundMap()), null)); + new com.infernalsuite.asp.skeleton.SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, new HashMap<>(), null)); } return chunkMap; } @@ -156,10 +163,15 @@ private static byte[] readCompressed(DataInputStream stream) throws IOException return decompressedData; } - private static CompoundTag readCompound(byte[] tagBytes) throws IOException { + private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { if (tagBytes.length == 0) return null; - NBTInputStream nbtStream = new NBTInputStream(new ByteArrayInputStream(tagBytes), NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - return (CompoundTag) nbtStream.readTag(); + 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/v11/v11WorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11WorldFormat.java new file mode 100644 index 000000000..779eeb28b --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v11/v11WorldFormat.java @@ -0,0 +1,7 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v11; + +public interface v11WorldFormat { + + com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(data -> data, new v11SlimeWorldDeSerializer()); + +} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java similarity index 51% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java index cb57b94a4..87faf2c36 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12SlimeWorldDeSerializer.java @@ -1,38 +1,36 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v12; +package com.infernalsuite.asp.serialization.slime.reader.impl.v12; -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.stream.NBTInputStream; import com.github.luben.zstd.Zstd; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.serialization.slime.reader.VersionedByteSlimeWorldReader; -import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; +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.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; -import java.nio.ByteOrder; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -public class v12SlimeWorldDeSerializer implements VersionedByteSlimeWorldReader { +public class v12SlimeWorldDeSerializer implements com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader { public static final int ARRAY_SIZE = 16 * 16 * 16 / (8 / 4); @@ -44,19 +42,19 @@ public SlimeWorld deserializeWorld(byte version, @Nullable SlimeLoader loader, S Long2ObjectMap chunks = readChunks(propertyMap, chunkBytes); byte[] extraTagBytes = readCompressed(dataStream); - CompoundTag extraTag = readCompound(extraTagBytes); + CompoundBinaryTag extraTag = readCompound(extraTagBytes); - SlimePropertyMap worldPropertyMap = propertyMap; - Optional propertiesMap = extraTag - .getAsCompoundTag("properties") - .map(CompoundTag::getValue); + ConcurrentMap extraData = new ConcurrentHashMap<>(); + if (extraTag != null) extraTag.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); - if (propertiesMap.isPresent()) { - worldPropertyMap = new SlimePropertyMap(propertiesMap.get()); + SlimePropertyMap worldPropertyMap = propertyMap; + if (extraData.containsKey("properties")) { + CompoundBinaryTag serializedSlimeProperties = (CompoundBinaryTag) extraData.get("properties"); + worldPropertyMap = SlimePropertyMap.fromCompound(serializedSlimeProperties); worldPropertyMap.merge(propertyMap); } - return new SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraTag, worldPropertyMap, worldVersion); + return new com.infernalsuite.asp.skeleton.SkeletonSlimeWorld(worldName, loader, readOnly, chunks, extraData, worldPropertyMap, worldVersion); } private static Long2ObjectMap readChunks(SlimePropertyMap slimePropertyMap, byte[] chunkBytes) throws IOException { @@ -99,49 +97,55 @@ private static Long2ObjectMap readChunks(SlimePropertyMap slimePrope // Block Data byte[] blockStateData = new byte[chunkData.readInt()]; chunkData.read(blockStateData); - CompoundTag blockStateTag = readCompound(blockStateData); + CompoundBinaryTag blockStateTag = readCompound(blockStateData); // Biome Data byte[] biomeData = new byte[chunkData.readInt()]; chunkData.read(biomeData); - CompoundTag biomeTag = readCompound(biomeData); + CompoundBinaryTag biomeTag = readCompound(biomeData); - chunkSections[sectionId] = new SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); + chunkSections[sectionId] = new com.infernalsuite.asp.skeleton.SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); } // HeightMaps byte[] heightMapData = new byte[chunkData.readInt()]; chunkData.read(heightMapData); - CompoundTag heightMaps = readCompound(heightMapData); + CompoundBinaryTag heightMaps = readCompound(heightMapData); // Tile Entities - byte[] tileEntities = read(chunkData); - - CompoundTag tileEntitiesCompound = readCompound(tileEntities); - @SuppressWarnings("unchecked") - List serializedTileEntities = ((ListTag) tileEntitiesCompound.getValue().get("tileEntities")).getValue(); + byte[] tileEntitiesRaw = read(chunkData); + List tileEntities; + CompoundBinaryTag tileEntitiesCompound = readCompound(tileEntitiesRaw); + if (tileEntitiesCompound == null) { + tileEntities = Collections.emptyList(); + } else { + tileEntities = tileEntitiesCompound.getList("tileEntities", BinaryTagTypes.COMPOUND).stream() + .map(tag -> (CompoundBinaryTag) tag) + .toList(); + } // Entities - byte[] entities = read(chunkData); - - CompoundTag entitiesCompound = readCompound(entities); - @SuppressWarnings("unchecked") - List serializedEntities = ((ListTag) entitiesCompound.getValue().get("entities")).getValue(); - + byte[] entitiesRaw = read(chunkData); + List entities; + CompoundBinaryTag entitiesCompound = readCompound(entitiesRaw); + if (entitiesCompound == null) { + entities = Collections.emptyList(); + } else { + entities = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND).stream() + .map(tag -> (CompoundBinaryTag) tag) + .toList(); + } // Extra Tag byte[] rawExtra = read(chunkData); - CompoundTag extra = readCompound(rawExtra); - // If the extra tag is empty, the serializer will save it as null. - // So if we deserialize a null extra tag, we will assume it was empty. - if (extra == null) { - extra = new CompoundTag("", new CompoundMap()); - } + CompoundBinaryTag extra = readCompound(rawExtra); - chunkMap.put(Util.chunkPosition(x, z), - new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, extra, null)); + Map extraData = new HashMap<>(); + if (extra != null) 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)); } return chunkMap; } @@ -163,12 +167,9 @@ private static byte[] read(DataInputStream stream) throws IOException { return data; } - private static CompoundTag readCompound(byte[] tagBytes) throws IOException { - if (tagBytes.length == 0) { - return null; - } + private static CompoundBinaryTag readCompound(byte[] tagBytes) throws IOException { + if (tagBytes.length == 0) return null; - NBTInputStream nbtStream = new NBTInputStream(new ByteArrayInputStream(tagBytes), NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - return (CompoundTag) nbtStream.readTag(); + return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(tagBytes)); } } diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12WorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12WorldFormat.java new file mode 100644 index 000000000..bb8057fba --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v12/v12WorldFormat.java @@ -0,0 +1,9 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v12; + +import com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat; + +public interface v12WorldFormat { + + SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v12SlimeWorldDeSerializer()); + +} diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/Upgrade.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/Upgrade.java new file mode 100644 index 000000000..41f9468a8 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/Upgrade.java @@ -0,0 +1,9 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; + +import com.infernalsuite.asp.api.SlimeDataConverter; + +public interface Upgrade { + + void upgrade(v1_9SlimeWorld world, SlimeDataConverter converter); + +} \ No newline at end of file diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java new file mode 100644 index 000000000..3831f05df --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java @@ -0,0 +1,121 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.upgrade; + +import com.infernalsuite.asp.api.SlimeDataConverter; +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.api.utils.NibbleArray; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.Upgrade; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; +import net.kyori.adventure.nbt.*; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +public class v1_13WorldUpgrade implements Upgrade { + + private static final int DATA_VERSION = 1631; + + @Override + public void upgrade(v1_9SlimeWorld world, SlimeDataConverter slimeDataConverter) { + Logger.getLogger("v1_13WorldUpgrade").warning("Updating world to the 1.13 format. This may take a while."); + + + List chunks = new ArrayList<>(world.chunks.values()); + long lastMessage = -1; + + for (int i = 0; i < chunks.size(); i++) { + v1_9SlimeChunk chunk = chunks.get(i); + + //Make sure that entities and tile entities are up-to-date with pre-1.13 + chunk.tileEntities = slimeDataConverter.convertTileEntities(chunk.tileEntities, world.getDataVersion(), 1343); + chunk.entities = slimeDataConverter.convertEntities(chunk.entities, world.getDataVersion(), 1343); + + // The world upgrade process is a very complex task, and there's already a + // built-in upgrade tool inside the server, so we can simply use it + CompoundBinaryTag.Builder globalTag = CompoundBinaryTag.builder(); + globalTag.put("DataVersion", IntBinaryTag.intBinaryTag(1343)); + + + CompoundBinaryTag.Builder chunkTag = CompoundBinaryTag.builder(); + + chunkTag.put("xPos", IntBinaryTag.intBinaryTag(chunk.x)); + chunkTag.put("zPos", IntBinaryTag.intBinaryTag(chunk.z)); + chunkTag.put("Sections", serializeSections(chunk.sections)); + chunkTag.put("Entities", ListBinaryTag.builder().add(chunk.entities).build()); + chunkTag.put("TileEntities", ListBinaryTag.builder().add(chunk.tileEntities).build()); + chunkTag.put("TileTicks", ListBinaryTag.empty()); + chunkTag.put("TerrainPopulated", ByteBinaryTag.byteBinaryTag((byte) 1)); + chunkTag.put("LightPopulated", ByteBinaryTag.byteBinaryTag((byte) 1)); + + globalTag.put("Level", chunkTag.build()); + + CompoundBinaryTag convertedTag = slimeDataConverter.convertChunkTo1_13(globalTag.build()); + CompoundBinaryTag convertedChunk = convertedTag.getCompound("Level"); + + // Chunk sections + v1_9SlimeChunkSection[] newSections = new v1_9SlimeChunkSection[16]; + ListBinaryTag serializedSections = convertedChunk.getList("Sections"); + + for (BinaryTag sectionTag : serializedSections) { + CompoundBinaryTag sectionCompound = (CompoundBinaryTag) sectionTag; + ListBinaryTag palette = sectionCompound.getList("Palette"); + long[] blockStates = sectionCompound.getLongArray("BlockStates"); + + NibbleArray blockLight = new NibbleArray(sectionCompound.getByteArray("BlockLight")); + NibbleArray skyLight = new NibbleArray(sectionCompound.getByteArray("SkyLight")); + + int index = sectionCompound.getInt("Y"); + + v1_9SlimeChunkSection section = new v1_9SlimeChunkSection(null, null, palette, blockStates, null, null, blockLight, skyLight); + newSections[index] = section; + } + + // Biomes + int[] newBiomes = new int[256]; + + for (int index = 0; index < chunk.biomes.length; index++) { + newBiomes[index] = chunk.biomes[index] & 255; + } + + chunk.sections = newSections; + chunk.biomes = newBiomes; + + // Upgrade data + chunk.upgradeData = convertedChunk.getCompound("UpgradeData"); + + int done = i + 1; + if (done == chunks.size()) { + Logger.getLogger("v1_13WorldUpgrade").info("World successfully converted to the 1.13 format!"); + } else if (System.currentTimeMillis() - lastMessage > 1000) { + int percentage = (done * 100) / chunks.size(); + Logger.getLogger("v1_13WorldUpgrade").info("Converting world... " + percentage + "%"); + lastMessage = System.currentTimeMillis(); + } + } + } + + private ListBinaryTag serializeSections(v1_9SlimeChunkSection[] sections) { + ListBinaryTag.@NotNull Builder builder = ListBinaryTag.builder(); + + for (int i = 0; i < sections.length; i++) { + v1_9SlimeChunkSection section = sections[i]; + + if (section != null) { + CompoundBinaryTag.Builder sectionTag = CompoundBinaryTag.builder(); + + sectionTag.put("Y", IntBinaryTag.intBinaryTag(i)); + sectionTag.put("Blocks", ByteArrayBinaryTag.byteArrayBinaryTag(section.blocks)); + sectionTag.put("Data", ByteArrayBinaryTag.byteArrayBinaryTag(section.data.getBacking())); + sectionTag.put("BlockLight", ByteArrayBinaryTag.byteArrayBinaryTag(section.blockLight.getBacking())); + sectionTag.put("SkyLight", ByteArrayBinaryTag.byteArrayBinaryTag(section.skyLight.getBacking())); + + builder.add(sectionTag.build()); + } + } + + return builder.build(); + } +} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java similarity index 65% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java index e83bfe63b..8c1f54730 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_16WorldUpgrade.java @@ -1,30 +1,41 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.upgrade; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.LongArrayTag; -import com.flowpowered.nbt.Tag; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; +import com.infernalsuite.asp.api.SlimeDataConverter; +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.LongArrayBinaryTag; import java.util.Arrays; +import java.util.Map; -public class v1_16WorldUpgrade implements Upgrade { +public class v1_16WorldUpgrade implements com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.Upgrade { + + private static final int DATA_VERSION = 2586; private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; @Override - public void upgrade(v1_9SlimeWorld world) { - for (v1_9SlimeChunk chunk : world.chunks.values()) { - // Add padding to height maps and block states - CompoundTag heightMaps = chunk.heightMap; + public void upgrade(v1_9SlimeWorld world, SlimeDataConverter slimeDataConverter) { + for (com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk chunk : world.chunks.values()) { + chunk.tileEntities = slimeDataConverter.convertTileEntities(chunk.tileEntities, world.getDataVersion(), DATA_VERSION); + chunk.entities = slimeDataConverter.convertEntities(chunk.entities, world.getDataVersion(), DATA_VERSION); + - for (Tag map : heightMaps.getValue().values()) { - if (map instanceof LongArrayTag arrayTag) { - arrayTag.setValue(addPadding(256, 9, arrayTag.getValue())); + // Add padding to height maps and block states + CompoundBinaryTag heightMaps = CompoundBinaryTag.builder().build(); + + for (Map.Entry heightMapEntry : chunk.heightMap) { + if (heightMapEntry.getValue() instanceof LongArrayBinaryTag arrayTag) { + heightMaps.put(heightMapEntry.getKey(), LongArrayBinaryTag.longArrayBinaryTag( + addPadding(256, 9, arrayTag.value()) + )); + } else { + heightMaps.put(heightMapEntry.getKey(), heightMapEntry.getValue()); } } @@ -32,7 +43,9 @@ public void upgrade(v1_9SlimeWorld world) { v1_9SlimeChunkSection section = chunk.sections[sectionIndex]; if (section != null) { - int bitsPerBlock = Math.max(4, ceillog2(section.palette.getValue().size())); + section.palette = slimeDataConverter.convertBlockPalette(section.palette, world.getDataVersion(), DATA_VERSION); + + int bitsPerBlock = Math.max(4, ceillog2(section.palette.size())); if (!isPowerOfTwo(bitsPerBlock)) { section = new v1_9SlimeChunkSection(null, null, section.palette, diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java similarity index 65% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java index 009a79cd0..bdb69ad00 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/upgrade/v1_18WorldUpgrade.java @@ -1,18 +1,17 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.upgrade; -import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; +import com.infernalsuite.asp.api.SlimeDataConverter; +import net.kyori.adventure.nbt.*; import java.util.*; -public class v1_18WorldUpgrade implements Upgrade { +public class v1_18WorldUpgrade implements com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.Upgrade { private static final String[] BIOMES_BY_ID = new String[256]; // rip datapacks + private static final int DATA_VERSION = 2975; static { + //Unfortunately DFU only supports 1.18 biome upgrades on chunk conversion, so we have to do this manually BIOMES_BY_ID[0] = "minecraft:ocean"; BIOMES_BY_ID[1] = "minecraft:plains"; BIOMES_BY_ID[2] = "minecraft:desert"; @@ -146,103 +145,56 @@ public class v1_18WorldUpgrade implements Upgrade { } @Override - public void upgrade(v1_9SlimeWorld world) { - for (v1_9SlimeChunk chunk : world.chunks.values()) { - - // SpawnerSpawnDataFix - for (CompoundTag tileEntity : chunk.tileEntities) { - CompoundMap value = tileEntity.getValue(); - Optional id = tileEntity.getStringValue("id"); - if (id.equals(Optional.of("minecraft:mob_spawner"))) { - Optional> spawnPotentials = tileEntity.getAsListTag("SpawnPotentials"); - Optional spawnData = tileEntity.getAsCompoundTag("SpawnData"); - if (spawnPotentials.isPresent()) { - ListTag spawnPotentialsList = (ListTag) spawnPotentials.get(); - List spawnPotentialsListValue = spawnPotentialsList.getValue(); - for (CompoundTag spawnPotentialsTag : spawnPotentialsListValue) { - CompoundMap spawnPotentialsValue = spawnPotentialsTag.getValue(); - Optional weight = spawnPotentialsTag.getIntValue("Weight"); - if (weight.isPresent()) { - int weightVal = weight.get(); - spawnPotentialsValue.remove("Weight"); - spawnPotentialsValue.put("weight", new IntTag("weight", weightVal)); - } - Optional entity = spawnPotentialsTag.getAsCompoundTag("Entity"); - if (entity.isPresent()) { - CompoundTag entityTag = entity.get(); - spawnPotentialsValue.remove("Entity"); - entityTag.getValue(); - CompoundMap dataMap = new CompoundMap(); - dataMap.put(new CompoundTag("entity", entityTag.getValue())); - spawnPotentialsValue.put("data", new CompoundTag("data", dataMap)); - } - } - value.put("SpawnPotentials", spawnPotentialsList); - if (!spawnPotentialsListValue.isEmpty()) { - CompoundTag compoundTag = spawnPotentialsListValue.get(0); - CompoundTag entityTag = compoundTag.getAsCompoundTag("data"). - get().getAsCompoundTag("entity").get(); - CompoundMap spawnDataMap = new CompoundMap(); - spawnDataMap.put(entityTag.clone()); - value.put("SpawnData", new CompoundTag("SpawnData", spawnDataMap)); - } - } else if (spawnData.isPresent()) { - CompoundTag spawnDataTag = spawnData.get(); - CompoundMap spawnDataValue = spawnDataTag.getValue(); - Optional entityTag = spawnDataTag.getAsCompoundTag("entity"); - Optional idTag = spawnDataTag.getAsStringTag("id"); - if (entityTag.isEmpty() && idTag.isPresent()) { - StringTag entityTypeTag = idTag.get(); - spawnDataValue.remove("id"); - CompoundMap entityMap = new CompoundMap(); - entityMap.put(entityTypeTag); - spawnDataValue.put("entity", new CompoundTag("entity", entityMap)); - value.put("SpawnData", spawnDataTag); - } - } - } - } + public void upgrade(com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld world, SlimeDataConverter converter) { + for (com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk chunk : world.chunks.values()) { + chunk.tileEntities = converter.convertTileEntities(chunk.tileEntities, world.getDataVersion(), DATA_VERSION); + chunk.entities = converter.convertEntities(chunk.entities, world.getDataVersion(), DATA_VERSION); - CompoundTag[] tags = createBiomeSections(chunk.biomes, false, 0); + CompoundBinaryTag[] tags = createBiomeSections(chunk.biomes, false, 0); - v1_9SlimeChunkSection[] sections = chunk.sections; + com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection[] sections = chunk.sections; for (int i = 0; i < sections.length; i++) { - v1_9SlimeChunkSection section = sections[i]; + com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection section = sections[i]; if (section == null) { continue; } - + section.palette = converter.convertBlockPalette(section.palette, world.getDataVersion(), DATA_VERSION); section.blockStatesTag = wrapPalette(section.palette, section.blockStates); section.biomeTag = tags[i]; } - v1_9SlimeChunkSection[] shiftedSections = new v1_9SlimeChunkSection[sections.length + 4]; + com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection[] shiftedSections = new com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection[sections.length + 4]; System.arraycopy(sections, 0, shiftedSections, 4, sections.length); chunk.sections = shiftedSections; // Shift all sections up 4 - v1_9SlimeChunkSection[] sectionArray = chunk.sections; + com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection[] sectionArray = chunk.sections; - CompoundMap emptyBiomes = new CompoundMap(); - emptyBiomes.put("palette", new ListTag<>("palette", TagType.TAG_STRING, List.of(new StringTag("", "minecraft:plains")))); + CompoundBinaryTag emptyBiomes = CompoundBinaryTag.builder() + .put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, List.of(StringBinaryTag.stringBinaryTag("minecraft:plains")))) + .build(); + + CompoundBinaryTag blocks = CompoundBinaryTag.builder() + .put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, List.of( + CompoundBinaryTag.builder() + .put("Name", StringBinaryTag.stringBinaryTag("minecraft:air")) + .build() + ))) + .build(); - CompoundMap blocks = new CompoundMap(); - emptyBiomes.put("palette", new ListTag<>("palette", TagType.TAG_STRING, List.of(new StringTag("", "minecraft:air")))); - CompoundTag blockTag = new CompoundTag("", blocks); - CompoundTag emptyBiomesTag = new CompoundTag("", emptyBiomes); for (int i = 0; i < sectionArray.length; i++) { - v1_9SlimeChunkSection section = sectionArray[i]; + com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection section = sectionArray[i]; if (section == null) { - sectionArray[i] = new v1_9SlimeChunkSection( + sectionArray[i] = new com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection( null, null, null, null, - blockTag, - emptyBiomesTag, + blocks, + emptyBiomes, null, null ); @@ -251,8 +203,8 @@ public void upgrade(v1_9SlimeWorld world) { } } - private static CompoundTag[] createBiomeSections(int[] biomes, final boolean wantExtendedHeight, final int minSection) { - final CompoundTag[] ret = new CompoundTag[wantExtendedHeight ? 24 : 16]; + private static CompoundBinaryTag[] createBiomeSections(int[] biomes, final boolean wantExtendedHeight, final int minSection) { + final CompoundBinaryTag[] ret = new CompoundBinaryTag[wantExtendedHeight ? 24 : 16]; if (biomes != null && biomes.length == 1536) { // magic value for 24 sections of biomes (24 * 4^3) //isAlreadyExtended.setValue(true); @@ -278,11 +230,11 @@ private static CompoundTag[] createBiomeSections(int[] biomes, final boolean wan // } // } } else { - ArrayList palette = new ArrayList<>(); - palette.add(new StringTag("", "minecraft:plains")); + ArrayList palette = new ArrayList<>(); + palette.add(StringBinaryTag.stringBinaryTag("minecraft:plains")); for (int i = 0; i < ret.length; ++i) { - ret[i] = wrapPalette(new ListTag<>("", TagType.TAG_STRING, palette).clone(), null); // copy palette so that later possible modifications don't trash all sections + ret[i] = wrapPalette(ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, palette), null); // copy palette so that later possible modifications don't trash all sections } } @@ -293,7 +245,7 @@ public static int ceilLog2(final int value) { return value == 0 ? 0 : Integer.SIZE - Integer.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros } - private static CompoundTag createBiomeSection(final int[] biomes, final int offset, final int mask) { + private static CompoundBinaryTag createBiomeSection(final int[] biomes, final int offset, final int mask) { final Map paletteId = new HashMap<>(); for (int idx = 0; idx < 64; ++idx) { @@ -301,7 +253,7 @@ private static CompoundTag createBiomeSection(final int[] biomes, final int offs paletteId.putIfAbsent(biome, paletteId.size()); } - List paletteString = new ArrayList<>(); + List paletteString = new ArrayList<>(); for (final Iterator iterator = paletteId.keySet().iterator(); iterator.hasNext(); ) { final int biomeId = iterator.next(); String biome = biomeId >= 0 && biomeId < BIOMES_BY_ID.length ? BIOMES_BY_ID[biomeId] : null; @@ -310,12 +262,12 @@ private static CompoundTag createBiomeSection(final int[] biomes, final int offs biome = update; } - paletteString.add(new StringTag("", biome == null ? "minecraft:plains" : biome)); + paletteString.add(StringBinaryTag.stringBinaryTag(biome == null ? "minecraft:plains" : biome)); } final int bitsPerObject = ceilLog2(paletteString.size()); if (bitsPerObject == 0) { - return wrapPalette(new ListTag<>("", TagType.TAG_STRING, paletteString), null); + return wrapPalette(ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, paletteString), null); } // manually create packed integer data @@ -346,19 +298,17 @@ private static CompoundTag createBiomeSection(final int[] biomes, final int offs packed[idx] = curr; } - return wrapPalette(new ListTag<>("", TagType.TAG_STRING, paletteString), packed); + return wrapPalette(ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, paletteString), packed); } - private static CompoundTag wrapPalette(ListTag palette, final long[] blockStates) { - CompoundMap map = new CompoundMap(); - CompoundTag tag = new CompoundTag("", map); - - map.put(new ListTag<>("palette", palette.getElementType(), palette.getValue())); + private static CompoundBinaryTag wrapPalette(ListBinaryTag palette, final long[] blockStates) { + CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() + .put("palette", palette); if (blockStates != null) { - map.put(new LongArrayTag("data", blockStates)); + builder.put("data", LongArrayBinaryTag.longArrayBinaryTag(blockStates)); } - return tag; + return builder.build(); } } \ No newline at end of file diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java similarity index 65% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java index 92fe6bbfb..c6dd2cc75 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunk.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; -import com.flowpowered.nbt.CompoundTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import java.util.List; @@ -11,12 +11,12 @@ public final class v1_9SlimeChunk { public v1_9SlimeChunkSection[] sections; public final int minY; public final int maxY; - public final CompoundTag heightMap; + public CompoundBinaryTag heightMap; public int[] biomes; - public final List tileEntities; - public final List entities; + public List tileEntities; + public List entities; // Used for 1.13 world upgrading - public CompoundTag upgradeData; + public CompoundBinaryTag upgradeData; v1_9SlimeChunk(String worldName, int x, @@ -24,10 +24,10 @@ public final class v1_9SlimeChunk { v1_9SlimeChunkSection[] sections, int minY, int maxY, - CompoundTag heightMap, + CompoundBinaryTag heightMap, int[] biomes, - List tileEntities, - List entities) { + List tileEntities, + List entities) { this.worldName = worldName; this.x = x; this.z = z; diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java similarity index 57% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java index 80f8fbb65..36dd48fb2 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeChunkSection.java @@ -1,8 +1,8 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.ListTag; -import com.infernalsuite.aswm.api.utils.NibbleArray; +import com.infernalsuite.asp.api.utils.NibbleArray; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; public class v1_9SlimeChunkSection { @@ -11,17 +11,17 @@ public class v1_9SlimeChunkSection { public final NibbleArray data; // Post 1.13 block data - public final ListTag palette; + public ListBinaryTag palette; public final long[] blockStates; // Post 1.17 block data - public CompoundTag blockStatesTag; - public CompoundTag biomeTag; + public CompoundBinaryTag blockStatesTag; + public CompoundBinaryTag biomeTag; public final NibbleArray blockLight; public final NibbleArray skyLight; - public v1_9SlimeChunkSection(byte[] blocks, NibbleArray data, ListTag palette, long[] blockStates, CompoundTag blockStatesTag, CompoundTag biomeTag, NibbleArray blockLight, NibbleArray skyLight) { + public v1_9SlimeChunkSection(byte[] blocks, NibbleArray data, ListBinaryTag palette, long[] blockStates, CompoundBinaryTag blockStatesTag, CompoundBinaryTag biomeTag, NibbleArray blockLight, NibbleArray skyLight) { this.blocks = blocks; this.data = data; this.palette = palette; diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java new file mode 100644 index 000000000..87656a073 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java @@ -0,0 +1,51 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; + +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.kyori.adventure.nbt.BinaryTag; + +import java.util.concurrent.ConcurrentMap; + +public class v1_9SlimeWorld { + + public byte version; + + public final String worldName; + public final SlimeLoader loader; + public final Long2ObjectMap chunks; + public final ConcurrentMap extraCompound; + public final SlimePropertyMap propertyMap; + public final boolean readOnly; + + public v1_9SlimeWorld(byte version, + String worldName, + SlimeLoader loader, + Long2ObjectMap chunks, + ConcurrentMap extraCompound, + SlimePropertyMap propertyMap, + boolean readOnly) { + this.version = version; + this.worldName = worldName; + this.loader = loader; + this.chunks = chunks; + this.extraCompound = extraCompound; + this.propertyMap = propertyMap; + this.readOnly = readOnly; + } + + public int getDataVersion() { + return switch (version) { + case 0x01 -> 99;//1.8; 99 as 1.8 does not have a dataversion yet and 100 is the first one + case 0x02 -> 184;//1.9.4 + case 0x03 -> 922;//1.11.2 + case 0x04 -> 1631;//1.13.2 + case 0x05 -> 1976;//1.14.4 + case 0x06 -> 2586;//1.16.5 + case 0x07 -> 2730;//1.17.1 + case 0x08 -> 2975;//1.18 + default -> throw new IllegalStateException("Unexpected value: " + version); + }; + } + +} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java similarity index 80% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java index 8401801fc..1086e2fd0 100644 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9SlimeWorldDeserializer.java @@ -1,36 +1,28 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.DoubleTag; -import com.flowpowered.nbt.IntArrayTag; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.TagType; -import com.flowpowered.nbt.stream.NBTInputStream; +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; + import com.github.luben.zstd.Zstd; -import com.infernalsuite.aswm.SlimeLogger; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.serialization.slime.reader.VersionedByteSlimeWorldReader; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.SlimeLogger; +import com.infernalsuite.asp.Util; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.utils.NibbleArray; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.nbt.*; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -class v1_9SlimeWorldDeserializer implements VersionedByteSlimeWorldReader { +class v1_9SlimeWorldDeserializer implements com.infernalsuite.asp.serialization.slime.reader.VersionedByteSlimeWorldReader { @Override public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String worldName, DataInputStream dataStream, SlimePropertyMap propertyMap, boolean readOnly) @@ -137,18 +129,20 @@ public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String Long2ObjectMap chunks = readChunks(worldVersion, version, worldName, minX, minZ, width, depth, chunkBitset, chunkData); // Entity deserialization - CompoundTag entitiesCompound = readCompoundTag(entities); + CompoundBinaryTag entitiesCompound = readCompoundTag(entities); - Long2ObjectMap> entityStorage = new Long2ObjectOpenHashMap<>(); + Long2ObjectMap> entityStorage = new Long2ObjectOpenHashMap<>(); if (entitiesCompound != null) { - List serializedEntities = ((ListTag) entitiesCompound.getValue().get("entities")).getValue(); + ListBinaryTag serializedEntities = entitiesCompound.getList("entities", BinaryTagTypes.COMPOUND); SlimeLogger.debug("Serialized entities: " + serializedEntities); - for (CompoundTag entityCompound : serializedEntities) { - ListTag listTag = (ListTag) entityCompound.getAsListTag("Pos").get(); + for (BinaryTag entityCompoundTag : serializedEntities) { + CompoundBinaryTag entityCompound = (CompoundBinaryTag) entityCompoundTag; - int chunkX = floor(listTag.getValue().get(0).getValue()) >> 4; - int chunkZ = floor(listTag.getValue().get(2).getValue()) >> 4; + ListBinaryTag listTag = entityCompound.getList("Pos"); + + int chunkX = floor(listTag.getDouble(0)) >> 4; + int chunkZ = floor(listTag.getDouble(2)) >> 4; long chunkKey = Util.chunkPosition(chunkX, chunkZ); v1_9SlimeChunk chunk = chunks.get(chunkKey); if (chunk != null) { @@ -157,7 +151,7 @@ public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String if (entityStorage.containsKey(chunkKey)) { entityStorage.get(chunkKey).add(entityCompound); } else { - List entityStorageList = new ArrayList<>(); + List entityStorageList = new ArrayList<>(); entityStorageList.add(entityCompound); entityStorage.put(chunkKey, entityStorageList); } @@ -165,13 +159,14 @@ public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String } // Tile Entity deserialization - CompoundTag tileEntitiesCompound = readCompoundTag(tileEntities); + CompoundBinaryTag tileEntitiesCompound = readCompoundTag(tileEntities); if (tileEntitiesCompound != null) { - ListTag tileEntitiesList = (ListTag) tileEntitiesCompound.getValue().get("tiles"); - for (CompoundTag tileEntityCompound : tileEntitiesList.getValue()) { - int chunkX = ((IntTag) tileEntityCompound.getValue().get("x")).getValue() >> 4; - int chunkZ = ((IntTag) tileEntityCompound.getValue().get("z")).getValue() >> 4; + ListBinaryTag tileEntitiesList = tileEntitiesCompound.getList("tiles"); + for (BinaryTag tileEntityCompoundTag : tileEntitiesList) { + CompoundBinaryTag tileEntityCompound = (CompoundBinaryTag) tileEntityCompoundTag; + int chunkX = tileEntityCompound.getInt("x") >> 4; + int chunkZ = tileEntityCompound.getInt("z") >> 4; v1_9SlimeChunk chunk = chunks.get(Util.chunkPosition(chunkX, chunkZ)); if (chunk == null) { @@ -183,10 +178,10 @@ public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String } // Extra Data - CompoundTag extraCompound = readCompoundTag(extraTag); + CompoundBinaryTag extraCompound = readCompoundTag(extraTag); if (extraCompound == null) { - extraCompound = new CompoundTag("", new CompoundMap()); + extraCompound = CompoundBinaryTag.empty(); } if (version <= 0x05) {} @@ -204,24 +199,30 @@ public v1_9SlimeWorld deserializeWorld(byte version, SlimeLoader loader, String // World properties SlimePropertyMap worldPropertyMap = propertyMap; - Optional propertiesMap = extraCompound - .getAsCompoundTag("properties") - .map(CompoundTag::getValue); + CompoundBinaryTag propertiesMap = extraCompound + .getCompound("properties"); + + if (!propertiesMap.isEmpty()) { + Map wpm = new HashMap<>(); + propertiesMap.forEach(entry -> wpm.put(entry.getKey(), entry.getValue())); - if (propertiesMap.isPresent()) { - worldPropertyMap = new SlimePropertyMap(propertiesMap.get()); + worldPropertyMap = new SlimePropertyMap(wpm); worldPropertyMap.merge(propertyMap); // Override world properties } else if (propertyMap == null) { // Make sure the property map is never null worldPropertyMap = new SlimePropertyMap(); } + + ConcurrentMap extraData = new ConcurrentHashMap<>(); + extraCompound.forEach(entry -> extraData.put(entry.getKey(), entry.getValue())); + return new v1_9SlimeWorld( worldVersion, worldName, loader, chunks, - extraCompound, - propertyMap, + extraData, + worldPropertyMap, readOnly ); } catch (EOFException ex) { @@ -244,7 +245,7 @@ private static Long2ObjectMap readChunks(byte worldVersion, int if (chunkBitset.get(bitsetIndex)) { // Height Maps - CompoundTag heightMaps; + CompoundBinaryTag heightMaps; if (worldVersion >= 0x04) { int heightMapsLength = dataStream.readInt(); @@ -254,7 +255,7 @@ private static Long2ObjectMap readChunks(byte worldVersion, int // Height Maps might be null if empty if (heightMaps == null) { - heightMaps = new CompoundTag("", new CompoundMap()); + heightMaps = CompoundBinaryTag.empty(); } } else { int[] heightMap = new int[256]; @@ -263,10 +264,8 @@ private static Long2ObjectMap readChunks(byte worldVersion, int heightMap[i] = dataStream.readInt(); } - CompoundMap map = new CompoundMap(); - map.put("heightMap", new IntArrayTag("heightMap", heightMap)); - - heightMaps = new CompoundTag("", map); + heightMaps = CompoundBinaryTag.builder() + .put("heightMap", IntArrayBinaryTag.intArrayBinaryTag(heightMap)).build(); } // Biome array @@ -350,11 +349,11 @@ private static ChunkSectionData readChunkSectionsNew(DataInputStream dataStream, // Block data byte[] blockStateData = new byte[dataStream.readInt()]; dataStream.read(blockStateData); - CompoundTag blockStateTag = readCompoundTag(blockStateData); + CompoundBinaryTag blockStateTag = readCompoundTag(blockStateData); byte[] biomeData = new byte[dataStream.readInt()]; dataStream.read(biomeData); - CompoundTag biomeTag = readCompoundTag(biomeData); + CompoundBinaryTag biomeTag = readCompoundTag(biomeData); // Sky Light Nibble Array NibbleArray skyLightArray; @@ -402,24 +401,26 @@ private static ChunkSectionData readChunkSections(DataInputStream dataStream, by byte[] blockArray; NibbleArray dataArray; - ListTag paletteTag; + ListBinaryTag paletteTag; long[] blockStatesArray; if (worldVersion >= 0x04) { // Post 1.13 // Palette int paletteLength = dataStream.readInt(); - List paletteList = new ArrayList<>(paletteLength); + List paletteList = new ArrayList<>(paletteLength); for (int index = 0; index < paletteLength; index++) { int tagLength = dataStream.readInt(); byte[] serializedTag = new byte[tagLength]; dataStream.read(serializedTag); - CompoundTag tag = readCompoundTag(serializedTag); + CompoundBinaryTag tag = readCompoundTag(serializedTag); paletteList.add(tag); } - paletteTag = new ListTag<>("", TagType.TAG_COMPOUND, paletteList); + paletteTag = ListBinaryTag.builder() + .add(paletteList) + .build(); // Block states int blockStatesArrayLength = dataStream.readInt(); @@ -469,13 +470,11 @@ private static ChunkSectionData readChunkSections(DataInputStream dataStream, by return new ChunkSectionData(chunkSectionArray, 0, 16); } - private static CompoundTag readCompoundTag(byte[] serializedCompound) throws IOException { + private static CompoundBinaryTag readCompoundTag(byte[] serializedCompound) throws IOException { if (serializedCompound.length == 0) { return null; } - NBTInputStream stream = new NBTInputStream(new ByteArrayInputStream(serializedCompound), NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - - return (CompoundTag) stream.readTag(); + return BinaryTagIO.unlimitedReader().read(new ByteArrayInputStream(serializedCompound)); } } diff --git a/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java new file mode 100644 index 000000000..62bf97f07 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java @@ -0,0 +1,7 @@ +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; + +public interface v1_9WorldFormat { + + com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat FORMAT = new com.infernalsuite.asp.serialization.slime.reader.impl.SimpleWorldFormat<>(new v1_v9SlimeConverter(), new v1_9SlimeWorldDeserializer()); + +} diff --git a/core/src/main/java/com/infernalsuite/aswm/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 similarity index 51% rename from core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java rename to core/src/main/java/com/infernalsuite/asp/serialization/slime/reader/impl/v1_9/v1_v9SlimeConverter.java index 5316bb507..64ed21953 100644 --- a/core/src/main/java/com/infernalsuite/aswm/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 @@ -1,33 +1,28 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; - -import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.serialization.SlimeWorldReader; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade.*; -import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; - +package com.infernalsuite.asp.serialization.slime.reader.impl.v1_9; + +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.serialization.SlimeWorldReader; +import com.infernalsuite.asp.serialization.slime.reader.impl.v1_9.upgrade.*; +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.skeleton.SkeletonSlimeWorld; +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 java.util.ArrayList; + import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; class v1_v9SlimeConverter implements SlimeWorldReader { public static final Map UPGRADES = new HashMap<>(); static { - UPGRADES.put((byte) 0x02, new v1_9WorldUpgrade()); - UPGRADES.put((byte) 0x03, new v1_11WorldUpgrade()); UPGRADES.put((byte) 0x04, new v1_13WorldUpgrade()); - UPGRADES.put((byte) 0x05, new v1_14WorldUpgrade()); UPGRADES.put((byte) 0x06, new v1_16WorldUpgrade()); - UPGRADES.put((byte) 0x07, new v1_17WorldUpgrade()); UPGRADES.put((byte) 0x08, new v1_18WorldUpgrade()); } @@ -43,30 +38,9 @@ public SlimeWorld readFromData(v1_9SlimeWorld data) { for (int i = 0; i < sections.length; i++) { v1_9SlimeChunkSection dataSection = slimeChunk.sections[i]; if (dataSection != null) { - // I'm not sure which upgrader should handle this, so I'm leaving it here - if (dataSection.biomeTag != null) { - ListTag palette = (ListTag) dataSection.biomeTag.getValue().get("palette"); - - ArrayList newPalette = new ArrayList(); - if (palette != null) { - for (StringTag stringTag : palette.getValue()) { - // air is no longer a valid biome, I'm not sure when this changed, - // so I cannot pick the proper upgrader to place it in. - if (stringTag.getValue().equals("minecraft:air")) continue; - newPalette.add(stringTag); - } - } - - if (palette == null || palette.getValue().isEmpty()) { - newPalette.add(new StringTag(null, "minecraft:plains")); - } - - dataSection.biomeTag.getValue().put("palette", new ListTag<>("palette", TagType.TAG_STRING, newPalette)); - } - sections[i] = new SlimeChunkSectionSkeleton( // SlimeChunkConverter can handle null blockState, but cannot handle empty blockState - dataSection.blockStatesTag.getValue().isEmpty() ? null : dataSection.blockStatesTag, + dataSection.blockStatesTag.isEmpty() ? null : dataSection.blockStatesTag, dataSection.biomeTag, dataSection.blockLight, dataSection.skyLight @@ -92,7 +66,7 @@ public SlimeWorld readFromData(v1_9SlimeWorld data) { slimeChunk.heightMap, slimeChunk.tileEntities, slimeChunk.entities, - new CompoundTag("", new CompoundMap()), + new HashMap<>(), slimeChunk.upgradeData )); } @@ -112,24 +86,17 @@ public SlimeWorld readFromData(v1_9SlimeWorld data) { public static int upgradeWorld(v1_9SlimeWorld world) { byte upgradeTo = 0x08; // Last version - int dataVersion = 3120; // MCVersions.V1_19_2 for (byte ver = (byte) (world.version + 1); ver <= upgradeTo; ver++) { Upgrade upgrade = UPGRADES.get(ver); if (upgrade == null) { - Logger.getLogger("v1_9WorldUpgrader").warning("Missing world upgrader for version " + ver + ". World will not be upgraded."); continue; } - upgrade.upgrade(world); - - if (ver == 0x08) { - dataVersion = 2975; - } + upgrade.upgrade(world, SlimeNMSBridge.instance().getSlimeDataConverter()); + world.version=ver; } - - world.version = 0x09; - return dataVersion; + return world.getDataVersion(); } } diff --git a/core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonCloning.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java similarity index 61% rename from core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonCloning.java rename to core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java index c6d42db7a..7f731fb2c 100644 --- a/core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonCloning.java +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonCloning.java @@ -1,25 +1,29 @@ -package com.infernalsuite.aswm.skeleton; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; - +package com.infernalsuite.asp.skeleton; + +import com.infernalsuite.asp.Util; +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 it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import java.util.*; +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 { - public static SkeletonSlimeWorld fullClone(String worldName, SlimeWorld world, SlimeLoader loader) { + public static SkeletonSlimeWorld fullClone(String worldName, SlimeWorld world, SlimeLoader loader, boolean readOnly) { return new SkeletonSlimeWorld(worldName, loader == null ? world.getLoader() : loader, - loader == null || world.isReadOnly(), + loader == null || readOnly, cloneChunkStorage(world.getChunkStorage()), - world.getExtraData().clone(), + new ConcurrentHashMap<>(world.getExtraData()), world.getPropertyMap().clone(), world.getDataVersion()); } @@ -36,7 +40,7 @@ public static SkeletonSlimeWorld weakCopy(SlimeWorld world) { world.getLoader(), world.isReadOnly(), cloned, - world.getExtraData().clone(), + new ConcurrentHashMap<>(world.getExtraData()), world.getPropertyMap().clone(), world.getDataVersion()); } @@ -56,8 +60,8 @@ private static Long2ObjectMap cloneChunkStorage(Collection 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 )); } return cloned; } - - private static List deepClone(List tags) { - List cloned = new ArrayList<>(tags.size()); - for (CompoundTag tag : tags) { - cloned.add(tag.clone()); - } - - return cloned; - } } diff --git a/core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonSlimeWorld.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonSlimeWorld.java similarity index 80% rename from core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonSlimeWorld.java rename to core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonSlimeWorld.java index 2ae1d623b..fdb7b85f9 100644 --- a/core/src/main/java/com/infernalsuite/aswm/skeleton/SkeletonSlimeWorld.java +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SkeletonSlimeWorld.java @@ -1,15 +1,16 @@ -package com.infernalsuite.aswm.skeleton; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.pdc.FlowPersistentDataContainer; -import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; +package com.infernalsuite.asp.skeleton; + +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.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.pdc.AdventurePersistentDataContainer; +import com.infernalsuite.asp.serialization.slime.SlimeSerializer; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,25 +18,25 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentMap; public final class SkeletonSlimeWorld implements SlimeWorld { private final String name; private final @Nullable SlimeLoader loader; private final boolean readOnly; private final Long2ObjectMap chunkStorage; - private final CompoundTag extraSerialized; + private final ConcurrentMap extraSerialized; private final SlimePropertyMap slimePropertyMap; private final int dataVersion; - private final FlowPersistentDataContainer pdc; + private final AdventurePersistentDataContainer pdc; public SkeletonSlimeWorld( String name, @Nullable SlimeLoader loader, boolean readOnly, Long2ObjectMap chunkStorage, - CompoundTag extraSerialized, + ConcurrentMap extraSerialized, SlimePropertyMap slimePropertyMap, int dataVersion ) { @@ -46,7 +47,7 @@ public SkeletonSlimeWorld( this.extraSerialized = extraSerialized; this.slimePropertyMap = slimePropertyMap; this.dataVersion = dataVersion; - this.pdc = new FlowPersistentDataContainer(extraSerialized); + this.pdc = new AdventurePersistentDataContainer(extraSerialized); } @Override @@ -70,12 +71,12 @@ public Collection getChunkStorage() { } @Override - public CompoundTag getExtraData() { + public ConcurrentMap getExtraData() { return this.extraSerialized; } @Override - public Collection getWorldMaps() { + public Collection getWorldMaps() { return List.of(); } @@ -118,7 +119,8 @@ public SlimeWorld clone(String worldName, SlimeLoader loader) throws WorldAlread } } - SlimeWorld cloned = SkeletonCloning.fullClone(worldName, this, loader); + //Make new worlds always non-read-only. if the provided loader is null, the fullClone method will set it to true again + SlimeWorld cloned = SkeletonCloning.fullClone(worldName, this, loader, false); if (loader != null) { loader.saveWorld(worldName, SlimeSerializer.serialize(cloned)); } @@ -147,7 +149,7 @@ public Long2ObjectMap chunkStorage() { return chunkStorage; } - public CompoundTag extraSerialized() { + public ConcurrentMap extraSerialized() { return extraSerialized; } diff --git a/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSectionSkeleton.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSectionSkeleton.java new file mode 100644 index 000000000..662937e29 --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSectionSkeleton.java @@ -0,0 +1,28 @@ +package com.infernalsuite.asp.skeleton; + +import com.infernalsuite.asp.api.utils.NibbleArray; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import org.jetbrains.annotations.Nullable; + +public record SlimeChunkSectionSkeleton(CompoundBinaryTag blockStates, CompoundBinaryTag biome, NibbleArray block, NibbleArray light) implements SlimeChunkSection { + @Override + public CompoundBinaryTag getBlockStatesTag() { + return this.blockStates; + } + + @Override + public CompoundBinaryTag getBiomeTag() { + return this.biome; + } + + @Override + public @Nullable NibbleArray getBlockLight() { + return this.block; + } + + @Override + public NibbleArray getSkyLight() { + return this.light; + } +} diff --git a/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java new file mode 100644 index 000000000..00268e96c --- /dev/null +++ b/core/src/main/java/com/infernalsuite/asp/skeleton/SlimeChunkSkeleton.java @@ -0,0 +1,57 @@ +package com.infernalsuite.asp.skeleton; + +import com.infernalsuite.asp.api.world.SlimeChunk; +import com.infernalsuite.asp.api.world.SlimeChunkSection; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; + +import java.util.List; +import java.util.Map; + +public record SlimeChunkSkeleton(int x, int z, SlimeChunkSection[] sections, + CompoundBinaryTag heightMap, + List blockEntities, + List entities, + Map extra, + CompoundBinaryTag upgradeData) implements SlimeChunk { + + @Override + public int getX() { + return this.x; + } + + @Override + public int getZ() { + return this.z; + } + + @Override + public SlimeChunkSection[] getSections() { + return this.sections; + } + + @Override + public CompoundBinaryTag getHeightMaps() { + return this.heightMap; + } + + @Override + public List getTileEntities() { + return this.blockEntities; + } + + @Override + public List getEntities() { + return this.entities; + } + + @Override + public Map getExtraData() { + return this.extra; + } + + @Override + public CompoundBinaryTag getUpgradeData() { + return this.upgradeData; + } +} diff --git a/core/src/main/java/com/infernalsuite/aswm/pdc/FlowDataTypeRegistry.java b/core/src/main/java/com/infernalsuite/aswm/pdc/FlowDataTypeRegistry.java deleted file mode 100644 index 2fb42c8e3..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/pdc/FlowDataTypeRegistry.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.infernalsuite.aswm.pdc; - -import com.flowpowered.nbt.*; -import com.google.common.base.Preconditions; -import com.google.common.primitives.Primitives; -import com.infernalsuite.aswm.api.SlimeNMSBridge; -import net.kyori.adventure.util.Services; -import org.bukkit.persistence.PersistentDataContainer; - -import java.util.ArrayList; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; -import java.util.function.Function; - -public class FlowDataTypeRegistry { - - public static final FlowDataTypeRegistry DEFAULT = new FlowDataTypeRegistry(); - private final Map, TagAdapter> adapters = new ConcurrentHashMap<>(); - - private > TagAdapter createAdapter(Class primitiveType, Class nbtBaseType, BiFunction builder, Function extractor) { - return new TagAdapter<>(primitiveType, nbtBaseType, builder, extractor); - } - - private > TagAdapter obtainAdapter(Class type) { - // Should be safe - //noinspection unchecked - return (TagAdapter) adapters.computeIfAbsent(type, this::createAdapter); - } - - /** - * Creates a TagAdapter object based on the given type. - *
- * Unlike CraftBukkit, this implementation does not allow complex tags, as it would require to cast them to CraftPersistentDataContainer which is not available from this context. - * - * @param type The class representing the type to be converted. - * @return A TagAdapter object that can convert the given type to the corresponding Tag implementation. - * @throws IllegalArgumentException if a valid TagAdapter implementation cannot be found for the requested type. - */ - private TagAdapter createAdapter(Class type) throws IllegalArgumentException { - if (!Primitives.isWrapperType(type)) { - type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types - } - // This would really make use of pattern matching in JDK 21 :( - - // Convert Byte to ByteTag - if (Objects.equals(Byte.class, type)) { - return createAdapter(Byte.class, ByteTag.class, ByteTag::new, ByteTag::getValue); - } - // Convert Short to ShortTag - if (Objects.equals(Short.class, type)) { - return createAdapter(Short.class, ShortTag.class, ShortTag::new, ShortTag::getValue); - } - // Convert Integer to IntTag - if (Objects.equals(Integer.class, type)) { - return createAdapter(Integer.class, IntTag.class, IntTag::new, IntTag::getValue); - } - // Convert Long to LongTag - if (Objects.equals(Long.class, type)) { - return createAdapter(Long.class, LongTag.class, LongTag::new, LongTag::getValue); - } - // Convert Float to FloatTag - if (Objects.equals(Float.class, type)) { - return createAdapter(Float.class, FloatTag.class, FloatTag::new, FloatTag::getValue); - } - // Convert Double to DoubleTag - if (Objects.equals(Double.class, type)) { - return createAdapter(Double.class, DoubleTag.class, DoubleTag::new, DoubleTag::getValue); - } - - // Convert String to StringTag - if (Objects.equals(String.class, type)) { - return createAdapter(String.class, StringTag.class, StringTag::new, StringTag::getValue); - } - - // Convert byte[] to ByteArrayTag - if (Objects.equals(byte[].class, type)) { - return createAdapter(byte[].class, ByteArrayTag.class, ByteArrayTag::new, ByteArrayTag::getValue); - } - // Convert int[] to IntArrayTag - if (Objects.equals(int[].class, type)) { - return createAdapter(int[].class, IntArrayTag.class, IntArrayTag::new, IntArrayTag::getValue); - } - // Convert long[] to LongArrayTag - if (Objects.equals(long[].class, type)) { - return createAdapter(long[].class, LongArrayTag.class, LongArrayTag::new, LongArrayTag::getValue); - } - // Convert short[] to ShortArrayTag - if (Objects.equals(short[].class, type)) { - return createAdapter(short[].class, ShortArrayTag.class, ShortArrayTag::new, ShortArrayTag::getValue); - } - - if (Objects.equals(PersistentDataContainer.class, type)) { - return createAdapter(PersistentDataContainer.class, CompoundTag.class, this::extractPDCIntoFlowNBT, this::extractFlowNBTIntoPDC); - } - - if (Objects.equals(PersistentDataContainer[].class, type)) { - return createAdapter(PersistentDataContainer[].class, ListTag.class, (key, value) -> { - var list = new ArrayList(); - - for (PersistentDataContainer pdc : value) { - list.add(extractPDCIntoFlowNBT(key, pdc)); - } - - return new ListTag<>(key, TagType.TAG_COMPOUND, list); - }, tag -> { - @SuppressWarnings("unchecked") var casted = (ListTag) tag; - var list = casted.getValue(); - var resArr = new PersistentDataContainer[list.size()]; - - for (int i = 0; i < list.size(); i++) { - resArr[i] = extractFlowNBTIntoPDC(list.get(i)); - } - - return resArr; - }); - } - - throw new IllegalArgumentException("Could not find a valid TagAdapter implementation for the requested type " + type.getSimpleName()); - } - - private PersistentDataContainer extractFlowNBTIntoPDC(CompoundTag compound) { - var optBridge = Services.service(SlimeNMSBridge.class); - if (optBridge.isPresent()) { - var bridge = optBridge.get(); - return bridge.extractCompoundMapIntoCraftPDC(compound.getValue()); - } else { - // Fall back to FlowPersistentDataContainer - var container = new FlowPersistentDataContainer(new CompoundTag("root", new CompoundMap()), this); - container.getRoot().getValue().putAll(compound.getValue()); - return container; - } - } - - private CompoundTag extractPDCIntoFlowNBT(String key, PersistentDataContainer pdc) { - var map = new CompoundMap(); - - if (pdc instanceof FlowPersistentDataContainer container) { - map.putAll(container.getRoot().getValue()); - } else { - Services.service(SlimeNMSBridge.class).orElseThrow().extractCraftPDC(pdc, map); - } - - return new CompoundTag(key, map); - } - - /** - * Wraps the passed value into a tag instance. - * - * @param type the type of the passed value - * @param value the value to be stored in the tag - * @param key the key to store the value under - * @param the generic type of the value - * @return the created tag instance - * @throws IllegalArgumentException if no suitable tag type adapter for this type was found - */ - public Tag wrap(String key, Class type, T value) throws IllegalArgumentException { - return obtainAdapter(type) - .build(key, value); - } - - /** - * Returns if the tag instance matches the provided primitive type. - * - * @param type the type of the primitive value - * @param base the base instance to check - * @param the generic type of the type - * @return if the base stores values of the primitive type passed - * @throws IllegalArgumentException if no suitable tag type adapter for this - * type was found - */ - public boolean isInstanceOf(Class type, Tag base) { - return obtainAdapter(type) - .isInstance(base); - } - - /** - * Extracts the value out of the provided tag. - * - * @param type the type of the value to extract - * @param tag the tag to extract the value from - * @param the generic type of the value stored inside the tag - * @return the extracted value - * @throws IllegalArgumentException if the passed base is not an instanced of the defined base type and therefore is not applicable to the extractor function - * @throws IllegalArgumentException if the found object is not of type passed - * @throws IllegalArgumentException if no suitable tag type adapter for this type was found - */ - public T extract(Class type, Tag tag) throws ClassCastException, IllegalArgumentException { - var adapter = obtainAdapter(type); - Preconditions.checkArgument(adapter.isInstance(tag), "The found tag instance (%s) cannot store %s", tag.getClass().getSimpleName(), type.getSimpleName()); - - Object foundValue = adapter.extract(tag); - Preconditions.checkArgument(type.isInstance(foundValue), "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), type.getSimpleName()); - return type.cast(foundValue); - } - - private record TagAdapter>(Class primitiveType, Class nbtBaseType, - BiFunction builder, Function extractor) { - /** - * This method will extract the value stored in the tag, according to - * the expected primitive type. - * - * @param base the base to extract from - * @return the value stored inside of the tag - * @throws ClassCastException if the passed base is not an instanced of - * the defined base type and therefore is not applicable to the - * extractor function - */ - T extract(Tag base) { - Preconditions.checkArgument(this.nbtBaseType.isInstance(base), "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), this.nbtBaseType.getSimpleName()); - return this.extractor.apply(this.nbtBaseType.cast(base)); - } - - /** - * Builds a tag instance wrapping around the provided value object. - * - * @param value the value to store inside the created tag - * @return the new tag instance - * @throws ClassCastException if the passed value object is not of the - * defined primitive type and therefore is not applicable to the builder - * function - */ - Z build(String key, Object value) { - Preconditions.checkArgument(this.primitiveType.isInstance(value), "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), this.primitiveType.getSimpleName()); - return this.builder.apply(key, this.primitiveType.cast(value)); - } - - /** - * Returns if the tag instance matches the adapters one. - * - * @param base the base to check - * @return if the tag was an instance of the set type - */ - boolean isInstance(Tag base) { - return this.nbtBaseType.isInstance(base); - } - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/pdc/FlowPersistentDataContainer.java b/core/src/main/java/com/infernalsuite/aswm/pdc/FlowPersistentDataContainer.java deleted file mode 100644 index deb69bfb1..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/pdc/FlowPersistentDataContainer.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.infernalsuite.aswm.pdc; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.Tag; -import com.flowpowered.nbt.stream.NBTInputStream; -import com.flowpowered.nbt.stream.NBTOutputStream; -import com.google.common.base.Preconditions; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataAdapterContext; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteOrder; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -public class FlowPersistentDataContainer implements PersistentDataContainer, PersistentDataAdapterContext { - - private final CompoundTag root; - private final FlowDataTypeRegistry registry; - - public FlowPersistentDataContainer(CompoundTag root, FlowDataTypeRegistry typeRegistry) { - this.root = root; - this.registry = typeRegistry; - } - - public FlowPersistentDataContainer(CompoundTag root) { - this.root = root; - this.registry = FlowDataTypeRegistry.DEFAULT; - } - - protected CompoundTag getRoot() { - return root; - } - - @Override - public void set(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull Z value) { - var name = key.toString(); - root.getValue().put(name, registry.wrap(name, type.getPrimitiveType(), type.toPrimitive(value, getAdapterContext()))); - } - - @Override - public boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { - var value = root.getValue().get(key.toString()); - - if (value == null) { - return false; - } - - return registry.isInstanceOf(type.getPrimitiveType(), value); - } - - @Override - public @Nullable Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType type) { - var value = root.getValue().get(key.toString()); - - if (value == null) { - return null; - } - - return type.fromPrimitive(registry.extract(type.getPrimitiveType(), (Tag) value), getAdapterContext()); - } - - @Override - public @NotNull Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType type, @NotNull Z defaultValue) { - var value = get(key, type); - return value == null ? defaultValue : value; - } - - @Override - public @NotNull Set getKeys() { - var keys = new HashSet(); - - for (String key : root.getValue().keySet()) { - String[] keyData = key.split(":", 2); - if (keyData.length == 2) { - keys.add(new NamespacedKey(keyData[0], keyData[1])); - } - } - - return keys; - } - - @Override - public void remove(@NotNull NamespacedKey key) { - root.getValue().remove(key.toString()); - } - - @Override - public boolean isEmpty() { - return root.getValue().isEmpty(); - } - - @Override - public void copyTo(@NotNull PersistentDataContainer other, boolean replace) { - Preconditions.checkNotNull(other, "The target container cannot be null"); - - if (other instanceof FlowPersistentDataContainer otherFlow) { - if (replace) { - otherFlow.root.setValue(this.root.getValue()); - } else { - otherFlow.root.getValue().forEach((k, v) -> otherFlow.root.getValue().putIfAbsent(k, v)); - } - } else { - throw new IllegalStateException("Cannot copy to a container that isn't a FlowPersistentDataContainer"); - } - } - - @Override - public @NotNull PersistentDataAdapterContext getAdapterContext() { - return this; - } - - @Override - public boolean has(@NotNull NamespacedKey key) { - return root.getValue().containsKey(key.toString()); - } - - @Override - public byte @NotNull [] serializeToBytes() throws IOException { - if (root == null || root.getValue().isEmpty()) { - return new byte[0]; - } - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); - NBTOutputStream outStream = new NBTOutputStream(outByteStream, NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - outStream.writeTag(root); - - return outByteStream.toByteArray(); - } - - @Override - public void readFromBytes(byte @NotNull [] bytes, boolean clear) throws IOException { - if (bytes.length == 0) { - return; - } - - NBTInputStream stream = new NBTInputStream(new ByteArrayInputStream(bytes), NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - var compound = (com.flowpowered.nbt.CompoundTag) stream.readTag(); - - if (clear) { - root.getValue().clear(); - } - - root.getValue().putAll(compound.getValue()); - } - - @Override - public @NotNull PersistentDataContainer newPersistentDataContainer() { - return new FlowPersistentDataContainer(new CompoundTag("root", new CompoundMap()), registry); - } - - @Override - public int hashCode() { - int hashCode = 3; - hashCode += root.hashCode(); // We will simply add the tag hashcode - return hashCode; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof FlowPersistentDataContainer flow)) { - return false; - } - - return Objects.equals(root, flow.root); - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/SlimeWorldReader.java b/core/src/main/java/com/infernalsuite/aswm/serialization/SlimeWorldReader.java deleted file mode 100644 index c90854f95..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/SlimeWorldReader.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.infernalsuite.aswm.serialization; - -import com.infernalsuite.aswm.api.world.SlimeWorld; - -public interface SlimeWorldReader { - - SlimeWorld readFromData(T data); -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilImportData.java b/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilImportData.java deleted file mode 100644 index cb05608f4..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilImportData.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.infernalsuite.aswm.serialization.anvil; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import org.jetbrains.annotations.Nullable; - -import java.io.File; - -public record AnvilImportData(File worldDir, String newName, @Nullable SlimeLoader loader) { -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilWorldReader.java b/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilWorldReader.java deleted file mode 100644 index af0119613..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/anvil/AnvilWorldReader.java +++ /dev/null @@ -1,434 +0,0 @@ -package com.infernalsuite.aswm.serialization.anvil; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.ListTag; -import com.flowpowered.nbt.TagType; -import com.flowpowered.nbt.stream.NBTInputStream; -import com.infernalsuite.aswm.Util; -import com.infernalsuite.aswm.api.exceptions.InvalidWorldException; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.serialization.SlimeWorldReader; -import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; - -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.zip.GZIPInputStream; -import java.util.zip.InflaterInputStream; - -public class AnvilWorldReader implements SlimeWorldReader { - - public static final int V1_16 = 2566; - public static final int V1_16_5 = 2586; - public static final int V1_17_1 = 2730; - public static final int V1_19_2 = 3120; - - private static final Pattern MAP_FILE_PATTERN = Pattern.compile("^(?:map_([0-9]*).dat)$"); - private static final int SECTOR_SIZE = 4096; - - public static final AnvilWorldReader INSTANCE = new AnvilWorldReader(); - - @Override - public SlimeWorld readFromData(AnvilImportData importData) { - File worldDir = importData.worldDir(); - try { - File levelFile = new File(worldDir, "level.dat"); - - if (!levelFile.exists() || !levelFile.isFile()) { - throw new RuntimeException(new InvalidWorldException(worldDir)); - } - - LevelData data = readLevelData(levelFile); - - // World version - int worldVersion = data.version; - - SlimePropertyMap propertyMap = new SlimePropertyMap(); - - File environmentDir = new File(worldDir, "DIM-1"); - propertyMap.setValue(SlimeProperties.ENVIRONMENT, "nether"); - if (!environmentDir.isDirectory()) { - environmentDir = new File(worldDir, "DIM1"); - propertyMap.setValue(SlimeProperties.ENVIRONMENT, "the_end"); - if (!environmentDir.isDirectory()) { - environmentDir = worldDir; - propertyMap.setValue(SlimeProperties.ENVIRONMENT, "normal"); - } - } - - // Chunks - File regionDir = new File(environmentDir, "region"); - - if (!regionDir.exists() || !regionDir.isDirectory()) { - throw new InvalidWorldException(environmentDir); - } - - Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); - - for (File file : Objects.requireNonNull(regionDir.listFiles((dir, name) -> name.endsWith(".mca")))) { - System.out.println("Loading region file: " + file.getName() + "..."); - if (file.exists()) { - chunks.putAll( - loadChunks(file, worldVersion).stream().collect(Collectors.toMap((chunk) -> Util.chunkPosition(chunk.getX(), chunk.getZ()), (chunk) -> chunk)) - ); - } - } - - // Entity serialization - { - File entityRegion = new File(environmentDir, "entities"); - if (entityRegion.exists()) { - for (File file : entityRegion.listFiles((dir, name) -> name.endsWith(".mca"))) { - if (file != null && file.exists()) { - loadEntities(file, worldVersion, chunks); - } - } - } - } - - if (chunks.isEmpty()) { - 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)); -// } -// } - - // Extra Data - CompoundMap extraData = new CompoundMap(); - - propertyMap.setValue(SlimeProperties.SPAWN_X, data.x); - propertyMap.setValue(SlimeProperties.SPAWN_Y, data.y); - propertyMap.setValue(SlimeProperties.SPAWN_Z, data.z); - - return new SkeletonSlimeWorld(importData.newName(), importData.loader(), true, chunks, new CompoundTag("", extraData), propertyMap, worldVersion); - } catch (IOException | InvalidWorldException e) { - - throw new RuntimeException(e); - } - } - - private static CompoundTag loadMap(File mapFile) throws IOException { - String fileName = mapFile.getName(); - int mapId = Integer.parseInt(fileName.substring(4, fileName.length() - 4)); - CompoundTag tag; - - try (NBTInputStream nbtStream = new NBTInputStream(new FileInputStream(mapFile), - NBTInputStream.GZIP_COMPRESSION, ByteOrder.BIG_ENDIAN)) { - tag = nbtStream.readTag().getAsCompoundTag().get().getAsCompoundTag("data").get(); - } - - tag.getValue().put("id", new IntTag("id", mapId)); - - return tag; - } - - private static LevelData readLevelData(File file) throws IOException, InvalidWorldException { - Optional tag; - - try (NBTInputStream nbtStream = new NBTInputStream(new FileInputStream(file))) { - tag = nbtStream.readTag().getAsCompoundTag(); - } - - if (tag.isPresent()) { - Optional dataTag = tag.get().getAsCompoundTag("Data"); - - if (dataTag.isPresent()) { - // Data version - int dataVersion = dataTag.get().getIntValue("DataVersion").orElse(-1); - - int spawnX = dataTag.get().getIntValue("SpawnX").orElse(0); - int spawnY = dataTag.get().getIntValue("SpawnY").orElse(255); - int spawnZ = dataTag.get().getIntValue("SpawnZ").orElse(0); - - return new LevelData(dataVersion, spawnX, spawnY, spawnZ); - } - } - - throw new InvalidWorldException(file.getParentFile()); - } - - private static void loadEntities(File file, int version, Long2ObjectMap chunkMap) throws IOException { - byte[] regionByteArray = Files.readAllBytes(file.toPath()); - DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(regionByteArray)); - - List chunks = new ArrayList<>(1024); - - for (int i = 0; i < 1024; i++) { - int entry = inputStream.readInt(); - int chunkOffset = entry >>> 8; - int chunkSize = entry & 15; - - if (entry != 0) { - ChunkEntry chunkEntry = new ChunkEntry(chunkOffset * SECTOR_SIZE, chunkSize * SECTOR_SIZE); - chunks.add(chunkEntry); - } - } - - for (ChunkEntry entry : chunks) { - try { - DataInputStream headerStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset(), entry.paddedSize())); - - int chunkSize = headerStream.readInt() - 1; - int compressionScheme = headerStream.readByte(); - - DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset() + 5, chunkSize)); - InputStream decompressorStream = compressionScheme == 1 ? new GZIPInputStream(chunkStream) : new InflaterInputStream(chunkStream); - NBTInputStream nbtStream = new NBTInputStream(decompressorStream, NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - CompoundTag globalCompound = (CompoundTag) nbtStream.readTag(); - CompoundMap globalMap = globalCompound.getValue(); - - - readEntityChunk(new CompoundTag("entityChunk", globalMap), version, chunkMap); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - } - - private static List loadChunks(File file, int worldVersion) throws IOException { - byte[] regionByteArray = Files.readAllBytes(file.toPath()); - DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(regionByteArray)); - - List chunks = new ArrayList<>(1024); - - for (int i = 0; i < 1024; i++) { - int entry = inputStream.readInt(); - int chunkOffset = entry >>> 8; - int chunkSize = entry & 15; - - if (entry != 0) { - ChunkEntry chunkEntry = new ChunkEntry(chunkOffset * SECTOR_SIZE, chunkSize * SECTOR_SIZE); - chunks.add(chunkEntry); - } - } - - return chunks.stream().map((entry) -> { - - try { - DataInputStream headerStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset(), entry.paddedSize())); - - int chunkSize = headerStream.readInt() - 1; - int compressionScheme = headerStream.readByte(); - - DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(regionByteArray, entry.offset() + 5, chunkSize)); - InputStream decompressorStream = compressionScheme == 1 ? new GZIPInputStream(chunkStream) : new InflaterInputStream(chunkStream); - NBTInputStream nbtStream = new NBTInputStream(decompressorStream, NBTInputStream.NO_COMPRESSION, ByteOrder.BIG_ENDIAN); - CompoundTag globalCompound = (CompoundTag) nbtStream.readTag(); - CompoundMap globalMap = globalCompound.getValue(); - - CompoundTag levelDataTag = new CompoundTag("Level", globalMap); - if (globalMap.containsKey("Level")) { - levelDataTag = (CompoundTag) globalMap.get("Level"); - } - - return readChunk(levelDataTag, worldVersion); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - - }).filter(Objects::nonNull).collect(Collectors.toList()); - } - - private static void readEntityChunk(CompoundTag compound, int worldVersion, Long2ObjectMap slimeChunkMap) { - int[] position = compound.getAsIntArrayTag("Position").orElseThrow().getValue(); - int chunkX = position[0]; - int chunkZ = position[1]; - - int dataVersion = compound.getAsIntTag("DataVersion").map(IntTag::getValue).orElse(-1); - if (dataVersion != worldVersion) { - System.err.println("Cannot load entity chunk at " + chunkX + "," + chunkZ + ": data version " + dataVersion + " does not match world version " + worldVersion); - return; - } - - SlimeChunk chunk = slimeChunkMap.get(Util.chunkPosition(chunkX, chunkZ)); - if (chunk == null) { - System.out.println("Lost entity chunk data at: " + chunkX + " " + chunkZ); - } else { - chunk.getEntities().addAll((List) compound.getAsListTag("Entities").get().getValue()); - } - } - - private static SlimeChunk readChunk(CompoundTag compound, int worldVersion) { - int chunkX = compound.getAsIntTag("xPos").get().getValue(); - int chunkZ = compound.getAsIntTag("zPos").get().getValue(); - - if (worldVersion >= V1_19_2) { // 1.18 chunks should have a DataVersion tag, we can check if the chunk has been converted to match the world - int dataVersion = compound.getAsIntTag("DataVersion").map(IntTag::getValue).orElse(-1); - if (dataVersion != worldVersion) { - System.err.println("Cannot load chunk at " + chunkX + "," + chunkZ + ": data version " + dataVersion + " does not match world version " + worldVersion); - return null; - } - } - - Optional status = compound.getStringValue("Status"); - - if (status.isPresent() && !status.get().equals("postprocessed") && !status.get().startsWith("full") && !status.get().startsWith("minecraft:full")) { - // It's a protochunk - return null; - } - -// int[] biomes; -// Tag biomesTag = compound.getValue().get("Biomes"); -// -// if (biomesTag instanceof IntArrayTag) { -// biomes = ((IntArrayTag) biomesTag).getValue(); -// } else if (biomesTag instanceof ByteArrayTag) { -// byte[] byteBiomes = ((ByteArrayTag) biomesTag).getValue(); -// biomes = toIntArray(byteBiomes); -// } else { -// biomes = null; -// } - - Optional optionalHeightMaps = compound.getAsCompoundTag("Heightmaps"); - CompoundTag heightMapsCompound = optionalHeightMaps.orElse(new CompoundTag("", new CompoundMap())); - - List tileEntities; - List entities; - ListTag sectionsTag; - - int minSectionY = 0; - int maxSectionY = 16; - - if (worldVersion < V1_19_2) { - tileEntities = ((ListTag) compound.getAsListTag("TileEntities") - .orElse(new ListTag<>("TileEntities", TagType.TAG_COMPOUND, new ArrayList<>()))).getValue(); - entities = ((ListTag) compound.getAsListTag("Entities") - .orElse(new ListTag<>("Entities", TagType.TAG_COMPOUND, new ArrayList<>()))).getValue(); - sectionsTag = (ListTag) compound.getAsListTag("Sections").get(); - } else { - tileEntities = ((ListTag) compound.getAsListTag("block_entities") - .orElse(new ListTag<>("block_entities", TagType.TAG_COMPOUND, new ArrayList<>()))).getValue(); - entities = ((ListTag) compound.getAsListTag("entities") - .orElse(new ListTag<>("entities", TagType.TAG_COMPOUND, new ArrayList<>()))).getValue(); - sectionsTag = (ListTag) compound.getAsListTag("sections").get(); - - Class type = compound.getValue().get("yPos").getValue().getClass(); - - if (type == Byte.class) { - minSectionY = compound.getByteValue("yPos").orElseThrow(); - } else { - minSectionY = compound.getIntValue("yPos").orElseThrow(); - } - - maxSectionY = sectionsTag.getValue().stream().map(c -> c.getByteValue("Y").orElseThrow()).max(Byte::compareTo).orElse((byte) 0) + 1; // Add 1 to the section, as we serialize it with the 1 added. - } - - SlimeChunkSection[] sectionArray = new SlimeChunkSection[maxSectionY - minSectionY]; - - for (CompoundTag sectionTag : sectionsTag.getValue()) { - int index = sectionTag.getByteValue("Y").get(); - - if (worldVersion < V1_17_1 && index < 0) { - // For some reason MC 1.14 worlds contain an empty section with Y = -1, however 1.17+ worlds can use these sections - continue; - } - - ListTag paletteTag = null; - long[] blockStatesArray = null; - - CompoundTag blockStatesTag = null; - CompoundTag biomeTag = null; - if (worldVersion < V1_19_2) { - paletteTag = (ListTag) sectionTag.getAsListTag("Palette").orElse(null); - blockStatesArray = sectionTag.getLongArrayValue("BlockStates").orElse(null); - - if (paletteTag == null || blockStatesArray == null || isEmpty(blockStatesArray)) { // Skip it - continue; - } - } else { - if (!sectionTag.getAsCompoundTag("block_states").isPresent() && !sectionTag.getAsCompoundTag("biomes").isPresent()) { - continue; // empty section - } - blockStatesTag = sectionTag.getAsCompoundTag("block_states").orElseThrow(); - biomeTag = sectionTag.getAsCompoundTag("biomes").orElseThrow(); - } - - NibbleArray blockLightArray = sectionTag.getValue().containsKey("BlockLight") ? new NibbleArray(sectionTag.getByteArrayValue("BlockLight").get()) : null; - NibbleArray skyLightArray = sectionTag.getValue().containsKey("SkyLight") ? new NibbleArray(sectionTag.getByteArrayValue("SkyLight").get()) : null; - - // There is no need to do any custom processing here. - sectionArray[index - minSectionY] = new SlimeChunkSectionSkeleton(/*paletteTag, blockStatesArray,*/ blockStatesTag, biomeTag, blockLightArray, skyLightArray); - } - - CompoundTag extraTag = new CompoundTag("", new CompoundMap()); - compound.getAsCompoundTag("ChunkBukkitValues").ifPresent(chunkBukkitValues -> extraTag.getValue().put(chunkBukkitValues)); - - for (SlimeChunkSection section : sectionArray) { - if (section != null) { // Chunk isn't empty - return new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMapsCompound, tileEntities, entities, extraTag, null); - } - } - - // Chunk is empty - return null; - } - - private static int[] toIntArray(byte[] buf) { - ByteBuffer buffer = ByteBuffer.wrap(buf).order(ByteOrder.BIG_ENDIAN); - int[] ret = new int[buf.length / 4]; - - buffer.asIntBuffer().get(ret); - - return ret; - } - - private static boolean isEmpty(byte[] array) { - for (byte b : array) { - if (b != 0) { - return false; - } - } - - return true; - } - - private static boolean isEmpty(long[] array) { - for (long b : array) { - if (b != 0L) { - return false; - } - } - - return true; - } - - - private record ChunkEntry(int offset, int paddedSize) { - - } - - private record LevelData(int version, int x, int y, int z) { - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10WorldFormat.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10WorldFormat.java deleted file mode 100644 index d56ecbf78..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v10/v10WorldFormat.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v10; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.serialization.slime.reader.impl.SimpleWorldFormat; - -public interface v10WorldFormat { - - // Latest, returns same - SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v10SlimeWorldDeSerializer()); - -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11WorldFormat.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11WorldFormat.java deleted file mode 100644 index aea58ec5a..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v11/v11WorldFormat.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v11; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.serialization.slime.reader.impl.SimpleWorldFormat; - -public interface v11WorldFormat { - - SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v11SlimeWorldDeSerializer()); - -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12WorldFormat.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12WorldFormat.java deleted file mode 100644 index 8fdef8408..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v12/v12WorldFormat.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v12; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.serialization.slime.reader.impl.SimpleWorldFormat; - -public interface v12WorldFormat { - - SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(data -> data, new v12SlimeWorldDeSerializer()); - -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/Upgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/Upgrade.java deleted file mode 100644 index fecd95b54..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/Upgrade.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; - -public interface Upgrade { - - void upgrade(v1_9SlimeWorld world); - -} \ No newline at end of file diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_11WorldUpgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_11WorldUpgrade.java deleted file mode 100644 index e1ee0b209..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_11WorldUpgrade.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; - -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.StringTag; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; - -import java.util.HashMap; -import java.util.Map; - -public class v1_11WorldUpgrade implements Upgrade { - - private static Map oldToNewMap = new HashMap<>(); - private static Map newToOldMap = new HashMap<>(); - - static { - rename("Furnace", "minecraft:furnace"); - rename("Chest", "minecraft:chest"); - rename("EnderChest", "minecraft:ender_chest"); - rename("RecordPlayer", "minecraft:jukebox"); - rename("Trap", "minecraft:dispenser"); - rename("Dropper", "minecraft:dropper"); - rename("Sign", "minecraft:sign"); - rename("MobSpawner", "minecraft:mob_spawner"); - rename("Music", "minecraft:noteblock"); - rename("Piston", "minecraft:piston"); - rename("Cauldron", "minecraft:brewing_stand"); - rename("EnchantTable", "minecraft:enchanting_table"); - rename("Airportal", "minecraft:end_portal"); - rename("Beacon", "minecraft:beacon"); - rename("Skull", "minecraft:skull"); - rename("DLDetector", "minecraft:daylight_detector"); - rename("Hopper", "minecraft:hopper"); - rename("Comparator", "minecraft:comparator"); - rename("FlowerPot", "minecraft:flower_pot"); - rename("Banner", "minecraft:banner"); - rename("Structure", "minecraft:structure_block"); - rename("EndGateway", "minecraft:end_gateway"); - rename("Control", "minecraft:command_block"); - rename(null, "minecraft:bed"); // Patch for issue s#62 - } - - private static void rename(String oldName, String newName) { - if (oldName != null) { - oldToNewMap.put(oldName, newName); - } - - newToOldMap.put(newName, oldName); - } - - @Override - public void upgrade(v1_9SlimeWorld world) { - // 1.11 changed the way Tile Entities are named - for (v1_9SlimeChunk chunk : world.chunks.values()) { - for (CompoundTag entityTag : chunk.tileEntities) { - String oldType = entityTag.getAsStringTag("id").get().getValue(); - String newType = oldToNewMap.get(oldType); - - if (newType == null) { - if (newToOldMap.containsKey(oldType)) { // Maybe it's in the new format for some reason? - continue; - } - - throw new IllegalStateException("Failed to find 1.11 upgrade for tile entity " + oldType); - } - - entityTag.getValue().put("id", new StringTag("id", newType)); - } - } - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java deleted file mode 100644 index afc9238ee..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_13WorldUpgrade.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; - -import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.api.SlimeNMSBridge; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; -import org.bukkit.ChatColor; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -public class v1_13WorldUpgrade implements Upgrade { - - @Override - public void upgrade(v1_9SlimeWorld world) { - Logger.getLogger("v1_13WorldUpgrade").warning("Updating world to the 1.13 format. This may take a while."); - - List chunks = new ArrayList<>(world.chunks.values()); - long lastMessage = -1; - - for (int i = 0; i < chunks.size(); i++) { - v1_9SlimeChunk chunk = chunks.get(i); - - // The world upgrade process is a very complex task, and there's already a - // built-in upgrade tool inside the server, so we can simply use it - CompoundTag globalTag = new CompoundTag("", new CompoundMap()); - globalTag.getValue().put("DataVersion", new IntTag("DataVersion", 1343)); - - CompoundTag chunkTag = new CompoundTag("Level", new CompoundMap()); - - chunkTag.getValue().put("xPos", new IntTag("xPos", chunk.x)); - chunkTag.getValue().put("zPos", new IntTag("zPos", chunk.z)); - chunkTag.getValue().put("Sections", serializeSections(chunk.sections)); - chunkTag.getValue().put("Entities", new ListTag<>("Entities", TagType.TAG_COMPOUND, chunk.entities)); - chunkTag.getValue().put("TileEntities", new ListTag<>("TileEntities", TagType.TAG_COMPOUND, chunk.tileEntities)); - chunkTag.getValue().put("TileTicks", new ListTag<>("TileTicks", TagType.TAG_COMPOUND, new ArrayList<>())); - chunkTag.getValue().put("TerrainPopulated", new ByteTag("TerrainPopulated", (byte) 1)); - chunkTag.getValue().put("LightPopulated", new ByteTag("LightPopulated", (byte) 1)); - - globalTag.getValue().put("Level", chunkTag); - - globalTag = SlimeNMSBridge.instance().convertChunkTo1_13(globalTag); - chunkTag = globalTag.getAsCompoundTag("Level").get(); - - // Chunk sections - v1_9SlimeChunkSection[] newSections = new v1_9SlimeChunkSection[16]; - ListTag serializedSections = (ListTag) chunkTag.getAsListTag("Sections").get(); - - for (CompoundTag sectionTag : serializedSections.getValue()) { - ListTag palette = (ListTag) sectionTag.getAsListTag("Palette").get(); - long[] blockStates = sectionTag.getLongArrayValue("BlockStates").get(); - - NibbleArray blockLight = new NibbleArray(sectionTag.getByteArrayValue("BlockLight").get()); - NibbleArray skyLight = new NibbleArray(sectionTag.getByteArrayValue("SkyLight").get()); - - int index = sectionTag.getIntValue("Y").get(); - - v1_9SlimeChunkSection section = new v1_9SlimeChunkSection(null, null, palette, blockStates, null, null, blockLight, skyLight); - newSections[index] = section; - } - - // Biomes - int[] newBiomes = new int[256]; - - for (int index = 0; index < chunk.biomes.length; index++) { - newBiomes[index] = chunk.biomes[index] & 255; - } - - chunk.sections = newSections; - chunk.biomes = newBiomes; - - // Upgrade data - chunk.upgradeData = chunkTag.getAsCompoundTag("UpgradeData").orElse(null); - - int done = i + 1; - if (done == chunks.size()) { - Logger.getLogger("v1_13WorldUpgrade").info("World successfully converted to the 1.13 format!"); - } else if (System.currentTimeMillis() - lastMessage > 1000) { - int percentage = (done * 100) / chunks.size(); - Logger.getLogger("v1_13WorldUpgrade").info("Converting world... " + percentage + "%"); - lastMessage = System.currentTimeMillis(); - } - } - } - - private ListTag serializeSections(v1_9SlimeChunkSection[] sections) { - ListTag sectionList = new ListTag<>("Sections", TagType.TAG_COMPOUND, new ArrayList<>()); - - for (int i = 0; i < sections.length; i++) { - v1_9SlimeChunkSection section = sections[i]; - - if (section != null) { - CompoundTag sectionTag = new CompoundTag(i + "", new CompoundMap()); - - sectionTag.getValue().put("Y", new IntTag("Y", i)); - sectionTag.getValue().put("Blocks", new ByteArrayTag("Blocks", section.blocks)); - sectionTag.getValue().put("Data", new ByteArrayTag("Data", section.data.getBacking())); - sectionTag.getValue().put("BlockLight", new ByteArrayTag("Data", section.blockLight.getBacking())); - sectionTag.getValue().put("SkyLight", new ByteArrayTag("Data", section.skyLight.getBacking())); - - sectionList.getValue().add(sectionTag); - } - } - - return sectionList; - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_14WorldUpgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_14WorldUpgrade.java deleted file mode 100644 index be0d1d931..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_14WorldUpgrade.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; - -import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; - -import java.util.*; - -public class v1_14WorldUpgrade implements Upgrade { - - private static final int[] VILLAGER_XP = { 0, 10, 50, 100, 150 }; - - private static Map oldToNewMap = new HashMap<>(); - private static Map newToOldMap = new HashMap<>(); - - static { - rename("minecraft:tube_coral_fan", "minecraft:tube_coral_wall_fan"); - rename("minecraft:brain_coral_fan", "minecraft:brain_coral_wall_fan"); - rename("minecraft:bubble_coral_fan", "minecraft:bubble_coral_wall_fan"); - rename("minecraft:fire_coral_fan", "minecraft:fire_coral_wall_fan"); - rename("minecraft:horn_coral_fan", "minecraft:horn_coral_wall_fan"); - rename("minecraft:stone_slab", "minecraft:smooth_stone_slab"); - rename("minecraft:sign", "minecraft:oak_sign"); - rename("minecraft:wall_sign", "minecraft:oak_wall_sign"); - } - - private static void rename(String oldName, String newName) { - oldToNewMap.put(oldName, newName); - newToOldMap.put(newName, oldName); - } - - @Override - public void upgrade(v1_9SlimeWorld world) { - for (v1_9SlimeChunk chunk : new ArrayList<>(world.chunks.values())) { - // Update renamed blocks - for (int sectionIndex = 0; sectionIndex < chunk.sections.length; sectionIndex++) { - v1_9SlimeChunkSection section = chunk.sections[sectionIndex]; - - if (section != null) { - List palette = section.palette.getValue(); - - for (int paletteIndex = 0; paletteIndex < palette.size(); paletteIndex++) { - CompoundTag blockTag = palette.get(paletteIndex); - String name = blockTag.getStringValue("Name").get(); - - // Trapped chests have now a different tile entity, - // so we have to update every block entity type - if (name.equals("minecraft:trapped_chest")) { - updateBlockEntities(chunk, sectionIndex, paletteIndex, "minecraft:chest", "minecraft:trapped_chest"); - } - - String newName = oldToNewMap.get(name); - - if (newName != null) { - blockTag.getValue().put("Name", new StringTag("Name", newName)); - } - } - } - } - - if (chunk.entities != null) { - for (CompoundTag entityTag : chunk.entities) { - String type = entityTag.getStringValue("id").get(); - - switch (type) { - case "minecraft:ocelot": - // Cats are no longer ocelots - int catType = entityTag.getIntValue("CatType").orElse(0); - - if (catType == 0) { - Optional owner = entityTag.getStringValue("Owner"); - Optional ownerId = entityTag.getStringValue("OwnerUUID"); - - if (owner.isPresent() || ownerId.isPresent()) { - entityTag.getValue().put("Trusting", new ByteTag("Trusting", (byte) 1)); - } - - entityTag.getValue().remove("CatType"); - } else if (catType > 0 && catType < 4) { - entityTag.getValue().put("id", new StringTag("id", "minecraft:cat")); - } - break; - case "minecraft:villager": - case "minecraft:zombie_villager": - // Villager data has changed - int profession = entityTag.getIntValue("Profession").orElse(0); - int career = entityTag.getIntValue("Career").orElse(0); - int careerLevel = entityTag.getIntValue("CareerLevel").orElse(1); - - // Villager level and xp has to be rebuilt - Optional offersOpt = entityTag.getAsCompoundTag("Offers"); - - if (offersOpt.isPresent()) { - if (careerLevel == 0 || careerLevel == 1) { - int amount = offersOpt.flatMap((offers) -> offers.getAsCompoundTag("Recipes")).map((recipes) -> recipes.getValue().size()).orElse(0); - careerLevel = clamp(amount / 2, 1, 5); - } - } - - Optional xp = entityTag.getAsCompoundTag("Xp"); - - if (!xp.isPresent()) { - entityTag.getValue().put("Xp", new IntTag("Xp", VILLAGER_XP[clamp(careerLevel - 1, 0, VILLAGER_XP.length - 1)])); - } - - entityTag.getValue().remove("Profession"); - entityTag.getValue().remove("Career"); - entityTag.getValue().remove("CareerLevel"); - - CompoundMap dataMap = new CompoundMap(); - dataMap.put("type", new StringTag("type", "minecraft:plains")); - dataMap.put("profession", new StringTag("profession", getVillagerProfession(profession, career))); - dataMap.put("level", new IntTag("level", careerLevel)); - - entityTag.getValue().put("VillagerData", new CompoundTag("VillagerData", dataMap)); - break; - case "minecraft:banner": - // The illager banners changed the translation message - Optional customName = entityTag.getStringValue("CustomName"); - - if (customName.isPresent()) { - String newName = customName.get().replace("\"translate\":\"block.minecraft.illager_banner\"", - "\"translate\":\"block.minecraft.ominous_banner\""); - - entityTag.getValue().put("CustomName", new StringTag("CustomName", newName)); - } - break; - } - } - } - } - } - - private int clamp(int i, int i1, int i2) { - return i < i1 ? i1 : (i > i2 ? i2 : i); - } - - private String getVillagerProfession(int profession, int career) { - return profession == 0 ? (career == 2 ? "minecraft:fisherman" : (career == 3 ? "minecraft:shepherd" : (career == 4 ? "minecraft:fletcher" : "minecraft:farmer"))) - : (profession == 1 ? (career == 2 ? "minecraft:cartographer" : "minecraft:librarian") : (profession == 2 ? "minecraft:cleric" : - (profession == 3 ? (career == 2 ? "minecraft:weaponsmith" : (career == 3 ? "minecraft:toolsmith" : "minecraft:armorer")) : - (profession == 4 ? (career == 2 ? "minecraft:leatherworker" : "minecraft:butcher") : (profession == 5 ? "minecraft:nitwit" : "minecraft:none"))))); - } - - private void updateBlockEntities(v1_9SlimeChunk chunk, int sectionIndex, int paletteIndex, String oldName, String newName) { - if (chunk.tileEntities != null) { - v1_9SlimeChunkSection section = chunk.sections[sectionIndex]; - long[] blockData = section.blockStates; - - int bitsPerBlock = Math.max(4, blockData.length * 64 / 4096); - long maxEntryValue = (1L << bitsPerBlock) - 1; - - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - int arrayIndex = y << 8 | z << 4 | x; - int bitIndex = arrayIndex * bitsPerBlock; - int startIndex = bitIndex / 64; - int endIndex = ((arrayIndex + 1) * bitsPerBlock - 1) / 64; - int startBitSubIndex = bitIndex % 64; - - int val; - - if (startIndex == endIndex) { - val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue); - } else { - int endBitSubIndex = 64 - startBitSubIndex; - val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue); - } - - // It's the right block type - if (val == paletteIndex) { - int blockX = x + chunk.x * 16; - int blockY = y + sectionIndex * 16; - int blockZ = z + chunk.z * 16; - - for (CompoundTag tileEntityTag : chunk.tileEntities) { - int tileX = tileEntityTag.getIntValue("x").get(); - int tileY = tileEntityTag.getIntValue("y").get(); - int tileZ = tileEntityTag.getIntValue("z").get(); - - if (tileX == blockX && tileY == blockY && tileZ == blockZ) { - String type = tileEntityTag.getStringValue("id").get(); - - if (!type.equals(oldName)) { - throw new IllegalStateException("Expected block entity to be " + oldName + ", not " + type); - } - - tileEntityTag.getValue().put("id", new StringTag("id", newName)); - break; - } - } - } - } - } - } - } - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_17WorldUpgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_17WorldUpgrade.java deleted file mode 100644 index 0149f6dc0..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_17WorldUpgrade.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.StringTag; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunkSection; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; - -import java.util.List; -import java.util.Optional; - -public class v1_17WorldUpgrade implements Upgrade { - - @Override - public void upgrade(v1_9SlimeWorld world) { - for (v1_9SlimeChunk chunk : world.chunks.values()) { - for (v1_9SlimeChunkSection section : chunk.sections) { - if (section == null) { - continue; - } - - List palette = section.palette.getValue(); - - for (CompoundTag blockTag : palette) { - Optional name = blockTag.getStringValue("Name"); - CompoundMap map = blockTag.getValue(); - - // CauldronRenameFix - if (name.equals(Optional.of("minecraft:cauldron"))) { - Optional properties = blockTag.getAsCompoundTag("Properties"); - if (properties.isPresent()) { - String waterLevel = blockTag.getStringValue("level").orElse("0"); - if (waterLevel.equals("0")) { - map.remove("Properties"); - } else { - map.put("Name", new StringTag("Name", "minecraft:water_cauldron")); - } - } - } - - // Renamed grass path item to dirt path - if (name.equals(Optional.of("minecraft:grass_path"))) { - map.put("Name", new StringTag("Name", "minecraft:dirt_path")); - } - } - } - } - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_9WorldUpgrade.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_9WorldUpgrade.java deleted file mode 100644 index b5b48ca2d..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/upgrade/v1_9WorldUpgrade.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.upgrade; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.StringTag; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.Upgrade; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeChunk; -import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9SlimeWorld; - -public class v1_9WorldUpgrade implements Upgrade { - - private static final JsonParser PARSER = new JsonParser(); - - @Override - public void upgrade(v1_9SlimeWorld world) { - // In 1.9, all signs must be formatted using JSON - for (v1_9SlimeChunk chunk : world.chunks.values()) { - for (CompoundTag entityTag : chunk.tileEntities) { - String type = entityTag.getAsStringTag("id").get().getValue(); - - if (type.equals("Sign")) { - CompoundMap map = entityTag.getValue(); - - for (int i = 1; i < 5; i++) { - String id = "Text" + i; - - map.put(id, new StringTag(id, fixJson(entityTag.getAsStringTag(id).map(StringTag::getValue).orElse(null)))); - } - } - } - } - } - - private static String fixJson(String value) { - if (value == null || value.equalsIgnoreCase("null") || value.isEmpty()) { - return "{\"text\":\"\"}"; - } - - try { - PARSER.parse(value); - } catch (JsonSyntaxException ex) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("text", value); - - return jsonObject.toString(); - } - - return value; - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java deleted file mode 100644 index cc59043a0..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9SlimeWorld.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; - -public class v1_9SlimeWorld { - - public byte version; - public final String worldName; - public final SlimeLoader loader; - public final Long2ObjectMap chunks; - public final CompoundTag extraCompound; - public final SlimePropertyMap propertyMap; - public final boolean readOnly; - - public v1_9SlimeWorld(byte version, - String worldName, - SlimeLoader loader, - Long2ObjectMap chunks, - CompoundTag extraCompound, - SlimePropertyMap propertyMap, - boolean readOnly) { - this.version = version; - this.worldName = worldName; - this.loader = loader; - this.chunks = chunks; - this.extraCompound = extraCompound; - this.propertyMap = propertyMap; - this.readOnly = readOnly; - } - - -} diff --git a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java b/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java deleted file mode 100644 index 8316cf534..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/serialization/slime/reader/impl/v1_9/v1_9WorldFormat.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9; - -import com.infernalsuite.aswm.serialization.slime.reader.impl.SimpleWorldFormat; - -public interface v1_9WorldFormat { - - SimpleWorldFormat FORMAT = new SimpleWorldFormat<>(new v1_v9SlimeConverter(), new v1_9SlimeWorldDeserializer()); - -} diff --git a/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSectionSkeleton.java b/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSectionSkeleton.java deleted file mode 100644 index afa22963a..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSectionSkeleton.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.infernalsuite.aswm.skeleton; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.utils.NibbleArray; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; -import org.jetbrains.annotations.Nullable; - -public record SlimeChunkSectionSkeleton(CompoundTag blockStates, CompoundTag biome, NibbleArray block, NibbleArray light) implements SlimeChunkSection { - @Override - public CompoundTag getBlockStatesTag() { - return this.blockStates; - } - - @Override - public CompoundTag getBiomeTag() { - return this.biome; - } - - @Override - public @Nullable NibbleArray getBlockLight() { - return this.block; - } - - @Override - public NibbleArray getSkyLight() { - return this.light; - } -} diff --git a/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSkeleton.java b/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSkeleton.java deleted file mode 100644 index 2aba39fb8..000000000 --- a/core/src/main/java/com/infernalsuite/aswm/skeleton/SlimeChunkSkeleton.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.infernalsuite.aswm.skeleton; - -import com.flowpowered.nbt.CompoundTag; -import com.infernalsuite.aswm.api.world.SlimeChunk; -import com.infernalsuite.aswm.api.world.SlimeChunkSection; - -import java.util.List; - -public record SlimeChunkSkeleton(int x, int z, SlimeChunkSection[] sections, - CompoundTag heightMap, - List blockEntities, - List entities, - CompoundTag extra, - CompoundTag upgradeData) implements SlimeChunk { - - @Override - public int getX() { - return this.x; - } - - @Override - public int getZ() { - return this.z; - } - - @Override - public SlimeChunkSection[] getSections() { - return this.sections; - } - - @Override - public CompoundTag getHeightMaps() { - return this.heightMap; - } - - @Override - public List getTileEntities() { - return this.blockEntities; - } - - @Override - public List getEntities() { - return this.entities; - } - - @Override - public CompoundTag getExtraData() { - return this.extra; - } - - @Override - public CompoundTag getUpgradeData() { - return this.upgradeData; - } -} diff --git a/gradle.properties b/gradle.properties index d002d3f01..b32ea63d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,12 @@ -group=com.infernalsuite.aswm -version=1.21.3-R0.1-SNAPSHOT +group=com.infernalsuite.asp +apiVersion=4.0.0-SNAPSHOT +version=1.21.4-R0.1-SNAPSHOT -mcVersion=1.21.3 -paperRef=da7138233f6392e791d790d1c3407414c855f9c2 +mcVersion=1.21.4 +paperRef=a838a886dcbc93664283034a41673e802a6b3098 org.gradle.caching=true org.gradle.parallel=true org.gradle.vfs.watch=false -org.gradle.jvmargs=-Xmx10g +org.gradle.jvmargs=-Xmx10g \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..0937f4663 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,53 @@ +[versions] +adventure = "4.17.0" +annotations = "26.0.1" +autoservice = "1.1.1" +blossom = "2.1.0" +bstats = "3.1.0" +cloud-core = "2.0.0" +cloud-minecraft = "2.0.0-beta.10" +configurate = "4.1.2" +gradle-git-properties = "2.4.2" +gradle-profiles = "0.54.0" +hikari = "6.2.1" +kotlin = "1.9.25" +lettuce = "6.5.1.RELEASE" +lombok = "1.18.36" +lombok-plugin = "8.11" +mongo = "5.2.1" +paperweight = "2.0.0-beta.14" +plugin-yml-paper = "0.6.0" +shadow = "8.3.5" +slf4j = "2.0.16" +zstd = "1.5.6-8" + +[plugins] +blossom = { id = "net.kyori.blossom", version.ref = "blossom" } +gitprops = { id = "com.gorylenko.gradle-git-properties", version.ref = "gradle-git-properties" } +profiles = { id = "org.kordamp.gradle.profiles", version.ref = "gradle-profiles" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +lombok = { id = "io.freefair.lombok", version.ref = "lombok-plugin" } +paperweight-patcher = { id = "io.papermc.paperweight.patcher", version.ref = "paperweight" } +plugin-yml-paper = { id = "net.minecrell.plugin-yml.paper", version.ref = "plugin-yml-paper" } +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } + +[libraries] +adventure-nbt = { module = "net.kyori:adventure-nbt", version.ref = "adventure" } +annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } +auto-service = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } +auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoservice" } +bstats = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } +cloud-annotations = { module = "org.incendo:cloud-annotations", version.ref = "cloud-core" } +cloud-bom = { module = "org.incendo:cloud-bom", version.ref = "cloud-core" } +cloud-core = { module = "org.incendo:cloud-core", version.ref = "cloud-core" } +cloud-minecraft-bom = { module = "org.incendo:cloud-minecraft-bom", version.ref = "cloud-minecraft" } +cloud-minecraft-extras = { module = "org.incendo:cloud-minecraft-extras", version.ref = "cloud-minecraft" } +cloud-paper = { module = "org.incendo:cloud-paper", version.ref = "cloud-minecraft" } +configurate-core = { module = "org.spongepowered:configurate-core", version.ref = "configurate" } +configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" } +hikari = { module = "com.zaxxer:HikariCP", version.ref = "hikari" } +lettuce = { module = "io.lettuce:lettuce-core", version.ref = "lettuce" } +lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } +mongo = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" } +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +zstd = { module = "com.github.luben:zstd-jni", version.ref = "zstd" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72b8..cea7a793a 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6..f3b75f3b0 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/importer/build.gradle.kts b/importer/build.gradle.kts index 66047469f..b69b82191 100644 --- a/importer/build.gradle.kts +++ b/importer/build.gradle.kts @@ -1,22 +1,27 @@ plugins { - id("com.gradleup.shadow") version "8.3.5" + id("asp.base-conventions") + id("com.gradleup.shadow") } dependencies { implementation(project(":api")) implementation(project(":core")) - implementation(project(":slimeworldmanager-api")) + implementation(project(":aspaper-api")) } tasks { jar { manifest { - attributes["Main-Class"] = "com.infernalsuite.aswm.importer.SWMImporter" + attributes["Main-Class"] = "com.infernalsuite.asp.importer.SWMImporter" } } shadowJar { + archiveClassifier.set("") minimize() } + assemble { + dependsOn(shadowJar) + } } -description = "slimeworldmanager-importer" +description = "asp-importer" diff --git a/importer/src/main/java/com/infernalsuite/aswm/importer/SWMImporter.java b/importer/src/main/java/com/infernalsuite/asp/importer/SWMImporter.java similarity index 87% rename from importer/src/main/java/com/infernalsuite/aswm/importer/SWMImporter.java rename to importer/src/main/java/com/infernalsuite/asp/importer/SWMImporter.java index 4f02aa0ba..97911f96d 100644 --- a/importer/src/main/java/com/infernalsuite/aswm/importer/SWMImporter.java +++ b/importer/src/main/java/com/infernalsuite/asp/importer/SWMImporter.java @@ -1,9 +1,8 @@ -package com.infernalsuite.aswm.importer; +package com.infernalsuite.asp.importer; -import com.infernalsuite.aswm.api.exceptions.InvalidWorldException; -import com.infernalsuite.aswm.serialization.anvil.AnvilImportData; -import com.infernalsuite.aswm.serialization.anvil.AnvilWorldReader; -import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; +import com.infernalsuite.asp.serialization.anvil.AnvilImportData; +import com.infernalsuite.asp.serialization.anvil.AnvilWorldReader; +import com.infernalsuite.asp.serialization.slime.SlimeSerializer; import java.io.File; import java.io.IOException; @@ -50,7 +49,7 @@ public static void main(String[] args) { public static void importWorld(File worldDir, File outputFile, boolean shouldPrintDebug) { try { outputFile.createNewFile(); - Files.write(outputFile.toPath(), SlimeSerializer.serialize(AnvilWorldReader.INSTANCE.readFromData(new AnvilImportData(worldDir, outputFile.getName(), null)))); + Files.write(outputFile.toPath(), SlimeSerializer.serialize(AnvilWorldReader.INSTANCE.readFromData(AnvilImportData.legacy(worldDir, outputFile.getName(), null)))); } catch (IndexOutOfBoundsException ex) { System.err.println("Oops, it looks like the world provided is too big to be imported. " + "Please trim it by using the MCEdit tool and try again."); diff --git a/loaders/api-loader/build.gradle.kts b/loaders/api-loader/build.gradle.kts new file mode 100644 index 000000000..d7f34aa1a --- /dev/null +++ b/loaders/api-loader/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") +} + +dependencies { + compileOnly(project(":api")) + compileOnly(paperApi()) +} + +publishConfiguration { + name = "Advanced Slime Paper API loader" + description = "HTTP-API based loader for Advanced Slime Paper" +} diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/api/APILoader.java b/loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/APILoader.java similarity index 97% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/api/APILoader.java rename to loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/APILoader.java index 93bc69a0a..10e7b6ec4 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/api/APILoader.java +++ b/loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/APILoader.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.loaders.api; +package com.infernalsuite.asp.loaders.api; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/api/MapStructure.java b/loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/MapStructure.java similarity index 97% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/api/MapStructure.java rename to loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/MapStructure.java index be56adb46..95bf1a783 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/api/MapStructure.java +++ b/loaders/api-loader/src/main/java/com/infernalsuite/asp/loaders/api/MapStructure.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.loaders.api; +package com.infernalsuite.asp.loaders.api; import java.util.List; import java.util.Map; diff --git a/loaders/build.gradle.kts b/loaders/build.gradle.kts index 79cfc7a9a..73dc43757 100644 --- a/loaders/build.gradle.kts +++ b/loaders/build.gradle.kts @@ -1,101 +1,21 @@ plugins { - id("java") - `java-library` - `maven-publish` - signing -} - -group = "com.infernalsuite.aswm" -version = "3.0.0-SNAPSHOT" - -repositories { - mavenCentral() + id("asp.base-conventions") + id("asp.publishing-conventions") } dependencies { compileOnly(project(":api")) - api("com.zaxxer:HikariCP:5.1.0") - api("org.mongodb:mongodb-driver-sync:5.1.0") - api("io.lettuce:lettuce-core:6.3.2.RELEASE") + api(project(":loaders:api-loader")) + api(project(":loaders:file-loader")) + api(project(":loaders:mongo-loader")) + api(project(":loaders:mysql-loader")) + api(project(":loaders:redis-loader")) - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - compileOnly("commons-io:commons-io:2.11.0") + compileOnly(paperApi()) } -profiles { - profile("publish") { - activation { - property { - setKey("publish") - setValue("true") - } - } - action { - publishing { - publications { - create("maven") { - groupId = "${project.group}" - artifactId = project.name - version = "${project.version}" - - from(components["java"]) - - pom { - name.set("Advanced Slime Paper Reference Loaders") - description.set("Reference loader implementations for ASP") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper") - licenses { - license { - name.set("GNU General Public License, Version 3.0") - url.set("https://www.gnu.org/licenses/gpl-3.0.txt") - } - } - developers { - developer { - id.set("InfernalSuite") - name.set("The InfernalSuite Team") - url.set("https://github.com/InfernalSuite") - email.set("infernalsuite@gmail.com") - } - } - scm { - connection.set("scm:git:https://github.com:InfernalSuite/AdvancedSlimePaper.git") - developerConnection.set("scm:git:ssh://github.com:InfernalSuite/AdvancedSlimePaper.git") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/") - } - issueManagement { - system.set("Github") - url.set("https://github.com/InfernalSuite/AdvancedSlimePaper/issues") - } - } - - versionMapping { - usage("java-api") { - fromResolutionOf("runtimeClasspath") - } - usage("java-runtime") { - fromResolutionResult() - } - } - } - } - repositories { - maven { - name = "infernalsuite" - url = uri("https://repo.infernalsuite.com/repository/maven-snapshots/") - credentials { - username = project.property("ISUsername") as String? - password = project.property("ISPassword") as String? - } - } - } - } - - signing { - useGpgCmd() - sign(publishing.publications["maven"]) - } - } - } -} \ No newline at end of file +publishConfiguration { + name = "Advanced Slime Paper Loaders" + description = "Default loaders for Advanced Slime Paper. There might be more loaders available then included in this BOM package" +} diff --git a/loaders/file-loader/build.gradle.kts b/loaders/file-loader/build.gradle.kts new file mode 100644 index 000000000..eabf68322 --- /dev/null +++ b/loaders/file-loader/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") +} + +dependencies { + compileOnly(project(":api")) + compileOnly(paperApi()) +} + +publishConfiguration { + name = "Advanced Slime Paper File Loader" + description = "File loader for Advanced Slime Paper" +} diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/file/FileLoader.java b/loaders/file-loader/src/main/java/com/infernalsuite/asp/loaders/file/FileLoader.java similarity index 93% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/file/FileLoader.java rename to loaders/file-loader/src/main/java/com/infernalsuite/asp/loaders/file/FileLoader.java index 0366a7b27..3fa872922 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/file/FileLoader.java +++ b/loaders/file-loader/src/main/java/com/infernalsuite/asp/loaders/file/FileLoader.java @@ -1,7 +1,7 @@ -package com.infernalsuite.aswm.loaders.file; +package com.infernalsuite.asp.loaders.file; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/loaders/mongo-loader/build.gradle.kts b/loaders/mongo-loader/build.gradle.kts new file mode 100644 index 000000000..c3861aed2 --- /dev/null +++ b/loaders/mongo-loader/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") +} + +dependencies { + compileOnly(project(":api")) + compileOnly(paperApi()) + + api(libs.mongo) +} + +publishConfiguration { + name = "Advanced Slime Paper MongoDB Loader" + description = "MongoDB GridFS Loader for Advanced Slime Paper" +} diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/mongo/MongoLoader.java b/loaders/mongo-loader/src/main/java/com/infernalsuite/asp/loaders/mongo/MongoLoader.java similarity index 94% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/mongo/MongoLoader.java rename to loaders/mongo-loader/src/main/java/com/infernalsuite/asp/loaders/mongo/MongoLoader.java index 4da311db6..533ffbdac 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/mongo/MongoLoader.java +++ b/loaders/mongo-loader/src/main/java/com/infernalsuite/asp/loaders/mongo/MongoLoader.java @@ -1,7 +1,7 @@ -package com.infernalsuite.aswm.loaders.mongo; +package com.infernalsuite.asp.loaders.mongo; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.loaders.UpdatableLoader; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.UpdatableLoader; import com.mongodb.MongoException; import com.mongodb.MongoNamespace; import com.mongodb.client.*; @@ -40,6 +40,18 @@ public MongoLoader(String database, String collection, @Nullable String username this.client = MongoClients.create(parsedUri); + init(); + } + + public MongoLoader(MongoClient client, String database, String collection) { + this.database = database; + this.collection = collection; + this.client = client; + + init(); + } + + private void init() { MongoDatabase mongoDatabase = client.getDatabase(database); MongoCollection mongoCollection = mongoDatabase.getCollection(collection); diff --git a/loaders/mysql-loader/build.gradle.kts b/loaders/mysql-loader/build.gradle.kts new file mode 100644 index 000000000..03df1323d --- /dev/null +++ b/loaders/mysql-loader/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") +} + +dependencies { + compileOnly(project(":api")) + + api(libs.hikari) + compileOnly(paperApi()) +} + +publishConfiguration { + name = "Advanced Slime Paper MySQL Loader" + description = "MySQL loader for Advanced Slime Paper" +} diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/mysql/MysqlLoader.java b/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java similarity index 96% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/mysql/MysqlLoader.java rename to loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java index 1a05d7c15..adf9383ba 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/mysql/MysqlLoader.java +++ b/loaders/mysql-loader/src/main/java/com/infernalsuite/asp/loaders/mysql/MysqlLoader.java @@ -1,7 +1,7 @@ -package com.infernalsuite.aswm.loaders.mysql; +package com.infernalsuite.asp.loaders.mysql; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.loaders.UpdatableLoader; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.UpdatableLoader; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.slf4j.Logger; @@ -80,7 +80,7 @@ public MysqlLoader(String sqlURL, String host, int port, String database, boolea } @Override - public void update() throws IOException, NewerDatabaseException { + public void update() throws IOException, NewerStorageException { try (Connection con = source.getConnection()) { int version; @@ -90,7 +90,7 @@ public void update() throws IOException, NewerDatabaseException { } if (version > CURRENT_DB_VERSION) { - throw new NewerDatabaseException(CURRENT_DB_VERSION, version); + throw new NewerStorageException(CURRENT_DB_VERSION, version); } if (version < CURRENT_DB_VERSION) { diff --git a/loaders/redis-loader/build.gradle.kts b/loaders/redis-loader/build.gradle.kts new file mode 100644 index 000000000..b41810de0 --- /dev/null +++ b/loaders/redis-loader/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("asp.base-conventions") + id("asp.publishing-conventions") +} + +dependencies { + compileOnly(project(":api")) + + api(libs.lettuce) + + compileOnly(paperApi()) +} + +publishConfiguration { + name = "Advanced Slime Paper Redis Loader" + description = "Redis loader for Advanced Slime Paper" +} diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/RedisLoader.java b/loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/RedisLoader.java similarity index 90% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/RedisLoader.java rename to loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/RedisLoader.java index f0f757662..b4f6c0d98 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/RedisLoader.java +++ b/loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/RedisLoader.java @@ -1,10 +1,10 @@ -package com.infernalsuite.aswm.loaders.redis; +package com.infernalsuite.asp.loaders.redis; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.loaders.redis.util.StringByteCodec; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; import io.lettuce.core.RedisClient; import io.lettuce.core.api.sync.RedisCommands; +import com.infernalsuite.asp.loaders.redis.util.StringByteCodec; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/util/StringByteCodec.java b/loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/util/StringByteCodec.java similarity index 95% rename from loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/util/StringByteCodec.java rename to loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/util/StringByteCodec.java index d9d4fadbf..6203b5a88 100644 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/redis/util/StringByteCodec.java +++ b/loaders/redis-loader/src/main/java/com/infernalsuite/asp/loaders/redis/util/StringByteCodec.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.loaders.redis.util; +package com.infernalsuite.asp.loaders.redis.util; import io.lettuce.core.codec.RedisCodec; diff --git a/loaders/src/main/java/com/infernalsuite/aswm/loaders/UpdatableLoader.java b/loaders/src/main/java/com/infernalsuite/aswm/loaders/UpdatableLoader.java deleted file mode 100644 index 23c0f5296..000000000 --- a/loaders/src/main/java/com/infernalsuite/aswm/loaders/UpdatableLoader.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.infernalsuite.aswm.loaders; - -import com.infernalsuite.aswm.api.loaders.SlimeLoader; - -import java.io.IOException; - -public abstract class UpdatableLoader implements SlimeLoader { - - public abstract void update() throws NewerDatabaseException, IOException; - - public class NewerDatabaseException extends Exception { - - private final int currentVersion; - private final int databaseVersion; - - - public NewerDatabaseException(int currentVersion, int databaseVersion) { - this.currentVersion = currentVersion; - this.databaseVersion = databaseVersion; - } - - public int getCurrentVersion() { - return currentVersion; - } - - public int getDatabaseVersion() { - return databaseVersion; - } - } -} diff --git a/patches/api/0001-Slime-World-Manager.patch b/patches/api/0001-Slime-World-Manager.patch deleted file mode 100644 index 059d62e51..000000000 --- a/patches/api/0001-Slime-World-Manager.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Mon, 26 Dec 2022 12:08:15 -0500 -Subject: [PATCH] Slime World Manager - - -diff --git a/build.gradle.kts b/build.gradle.kts -index e29e5024fa693baae469d47fe77b57118f14627c..4da167f928789c1b5d55da2749a94e44912492ad 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -1,3 +1,5 @@ -+ -+ - plugins { - `java-library` - `maven-publish` -@@ -41,6 +43,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider { - dependencies { - api("com.mojang:brigadier:1.2.9") // Paper - Brigadier command api - // api dependencies are listed transitively to API consumers -+ api(project(":api")) // ASWM - api("com.google.guava:guava:32.1.2-jre") - api("com.google.code.gson:gson:2.10.1") - // Paper start - adventure -diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java -index 652ff54e7c50412503725d628bfe72ed03059790..9c5118e6193b0e9852ef6b52cb4ae92ded1ba464 100644 ---- a/src/main/java/io/papermc/paper/ServerBuildInfo.java -+++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java -@@ -19,6 +19,11 @@ public interface ServerBuildInfo { - */ - Key BRAND_PAPER_ID = Key.key("papermc", "paper"); - -+ /** -+ * The brand id for AdvancedSlimePaper. -+ */ -+ Key BRAND_ADVANCED_SLIME_PAPER_ID = Key.key("infernalsuite", "advancedslimepaper"); -+ - /** - * Gets the {@code ServerBuildInfo}. - * diff --git a/patches/api/0002-Dont-close-Slime-Plugin-Classloader.patch b/patches/api/0002-Dont-close-Slime-Plugin-Classloader.patch deleted file mode 100644 index cd8036f16..000000000 --- a/patches/api/0002-Dont-close-Slime-Plugin-Classloader.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Sat, 28 Jan 2023 14:10:43 -0500 -Subject: [PATCH] Dont close Slime Plugin Classloader - - -diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java -index f9b57b872780aa6b9b959494874b57c7a8ff0c53..489a9dd31dd68325213110e5b2f144f7159b8d63 100644 ---- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java -@@ -265,7 +265,9 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm - // Paper end - super.close(); - } finally { -+ if (this.plugin == null || !this.plugin.getName().equals("SlimeWorldManager")) { // ASWM - Don't close - jar.close(); -+ } // ASWM - Don't close - } - } - diff --git a/patches/server/0001-Build-Changes.patch b/patches/server/0001-Build-Changes.patch deleted file mode 100644 index f075685c5..000000000 --- a/patches/server/0001-Build-Changes.patch +++ /dev/null @@ -1,2281 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Mon, 26 Dec 2022 11:25:35 -0500 -Subject: [PATCH] Build Changes - -Update shadow plugin - -diff --git a/build.gradle.kts b/build.gradle.kts -index faf3e3fd72e8c915e7a4803dacbe1bb576c6663e..e90c79790c40afb67364fed615be2384c30d73d0 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -4,6 +4,7 @@ import java.time.Instant - plugins { - java - `maven-publish` -+ id("com.gradleup.shadow") - } - - val log4jPlugins = sourceSets.create("log4jPlugins") -@@ -25,7 +26,13 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider { - // Paper end - configure mockito agent that is needed in newer java versions - - dependencies { -- implementation(project(":paper-api")) -+ // ASWM start -+ implementation(project(":slimeworldmanager-api")) -+ implementation(project(":core")) -+ implementation("io.papermc.paper:paper-mojangapi:1.20.4-R0.1-SNAPSHOT") { -+ exclude("io.papermc.paper", "paper-api") -+ } -+ // ASWM end - // Paper start - 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 -@@ -99,14 +106,14 @@ tasks.jar { - val gitBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() // Paper - attributes( - "Main-Class" to "org.bukkit.craftbukkit.Main", -- "Implementation-Title" to "Paper", -+ "Implementation-Title" to "AdvancedSlimePaper", - "Implementation-Version" to implementationVersion, - "Implementation-Vendor" to date, // Paper -- "Specification-Title" to "Paper", -+ "Specification-Title" to "AdvancedSlimePaper", - "Specification-Version" to project.version, -- "Specification-Vendor" to "Paper Team", -- "Brand-Id" to "papermc:paper", -- "Brand-Name" to "Paper", -+ "Specification-Vendor" to "InfernalSuite Team", -+ "Brand-Id" to "infernalsuite:advancedslimepaper", -+ "Brand-Name" to "AdvancedSlimePaper", - "Build-Number" to (build ?: ""), - "Build-Time" to Instant.now().toString(), - "Git-Branch" to gitBranch, // Paper -@@ -172,7 +179,7 @@ fun TaskContainer.registerRunTask( - name: String, - block: JavaExec.() -> Unit - ): TaskProvider = register(name) { -- group = "paper" -+ group = "slimeworldmanager" - mainClass.set("org.bukkit.craftbukkit.Main") - standardInput = System.`in` - workingDir = rootProject.layout.projectDirectory -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -index b3c993a790fc3fab6a408c731deb297f74c959ce..0bbd557602932b67212b8951ef769bbce70c5477 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -@@ -42,7 +42,7 @@ public final class ChunkEntitySlices { - private final EntityCollectionBySection hardCollidingEntities; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByType; -- private final EntityList entities = new EntityList(); -+ public final EntityList entities = new EntityList(); //ASWM - - public FullChunkStatus status; - public final ChunkData chunkData; -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -index 91a6f57f35fc1553159cca138a0619e703b2b014..07fee642805ac3ec6fce489a0b9050ddf941d932 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -@@ -185,7 +185,8 @@ public final class ChunkHolderManager { - }; - } - -- public void close(final boolean save, final boolean halt) { -+ public void close(boolean save, final boolean halt) { // ASWM -+ if (this.world instanceof com.infernalsuite.aswm.level.SlimeLevelInstance) save = false; // ASWM - TickThread.ensureTickThread("Closing world off-main"); - if (halt) { - LOGGER.info("Waiting 60s for chunk system to halt for world '" + WorldUtil.getWorldName(this.world) + "'"); -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java -index 1440c9e2b106616884edcb20201113320817ed9f..158dee86d783c4a0a0479b07c4159184479b2a65 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java -@@ -6,6 +6,7 @@ import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock; - import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; - import ca.spottedleaf.concurrentutil.util.Priority; - import ca.spottedleaf.moonrise.common.PlatformHooks; -+import com.infernalsuite.aswm.level.CommonLoadTask; - import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemConverters; - import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; - import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; -@@ -32,8 +33,8 @@ public final class ChunkLoadTask extends ChunkProgressionTask { - - private static final Logger LOGGER = LoggerFactory.getLogger(ChunkLoadTask.class); - -- private final NewChunkHolder chunkHolder; -- private final ChunkDataLoadTask loadTask; -+ public final NewChunkHolder chunkHolder; // ASWM -+ private final CommonLoadTask loadTask; - - private volatile boolean cancelled; - private NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask; -@@ -45,11 +46,20 @@ public final class ChunkLoadTask extends ChunkProgressionTask { - final NewChunkHolder chunkHolder, final Priority priority) { - super(scheduler, world, chunkX, chunkZ); - this.chunkHolder = chunkHolder; -- this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); -- this.loadTask.addCallback((final GenericDataLoadTask.TaskResult result) -> { -- ChunkLoadTask.this.loadResult = result; // must be before getAndDecrement -- ChunkLoadTask.this.tryCompleteLoad(); -- }); -+ // ASWM start -+ if (world instanceof com.infernalsuite.aswm.level.SlimeLevelInstance levelInstance) { -+ -+ this.loadTask = levelInstance.getLoadTask(this, scheduler, world, chunkX, chunkZ, priority, result -> { -+ ChunkLoadTask.this.complete(result == null ? null : result.left(), result == null ? null : result.right()); -+ }); -+ } else { -+ ChunkDataLoadTask task = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority); -+ task.addCallback((final GenericDataLoadTask.TaskResult result) -> { -+ ChunkLoadTask.this.complete(result == null ? null : result.left(), result == null ? null : result.right()); -+ }); -+ this.loadTask = task; -+ } -+ // ASWM end - } - - private void tryCompleteLoad() { -@@ -276,7 +286,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask { - - private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {} - -- private static final class ChunkDataLoadTask extends CallbackDataLoadTask { -+ private static final class ChunkDataLoadTask extends CallbackDataLoadTask implements CommonLoadTask { // ASWM - private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, - final int chunkZ, final Priority priority) { - super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority); -diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -index 532306cacd52579cdf37e4aca25887b1ed3ba6a1..55864e5f9ba2ccca0160760e90d8f77819688b8e 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 { - private static final Logger LOGGER = LogUtils.getClassLogger(); - private static final int DISTANCE_ERROR = -1; - private static final int DISTANCE_UNKNOWN = -2; -- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper"; -+ private static final String DOWNLOAD_PAGE = "https://discord.gg/YevvsMa"; - - @Override - public long getCacheTime() { -@@ -49,7 +49,7 @@ public class PaperVersionFetcher implements VersionFetcher { - if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) { - updateMessage = text("You are running a development version without access to version information", color(0xFF5300)); - } else { -- updateMessage = getUpdateStatusMessage("PaperMC/Paper", build); -+ updateMessage = getUpdateStatusMessage("InfernalSuite/AdvancedSlimePaper", build); - } - final @Nullable Component history = this.getHistory(); - -@@ -58,17 +58,18 @@ public class PaperVersionFetcher implements VersionFetcher { - - private static Component getUpdateStatusMessage(final String repo, final ServerBuildInfo build) { - int distance = DISTANCE_ERROR; -- -+ /* // AdvancedSlimePaper start - final OptionalInt buildNumber = build.buildNumber(); - if (buildNumber.isPresent()) { - distance = fetchDistanceFromSiteApi(build, buildNumber.getAsInt()); - } else { -+ */ // AdvancedSlimePaper end - final Optional gitBranch = build.gitBranch(); - final Optional gitCommit = build.gitCommit(); - if (gitBranch.isPresent() && gitCommit.isPresent()) { - distance = fetchDistanceFromGitHub(repo, gitBranch.get(), gitCommit.get()); - } -- } -+ //} // AdvancedSlimePaper - - return switch (distance) { - case DISTANCE_ERROR -> text("Error obtaining version information", NamedTextColor.YELLOW); -@@ -76,7 +77,7 @@ public class PaperVersionFetcher implements VersionFetcher { - case DISTANCE_UNKNOWN -> text("Unknown version", NamedTextColor.YELLOW); - default -> text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW) - .append(Component.newline()) -- .append(text("Download the new version at: ") -+ .append(text("Download the new version from our Discord: ") - .append(text(DOWNLOAD_PAGE, NamedTextColor.GOLD) - .hoverEvent(text("Click to open", NamedTextColor.WHITE)) - .clickEvent(ClickEvent.openUrl(DOWNLOAD_PAGE)))); -diff --git a/src/main/java/com/infernalsuite/aswm/Converter.java b/src/main/java/com/infernalsuite/aswm/Converter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7d1753c0b7e89bbf0c245a0231b62773eca2779e ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/Converter.java -@@ -0,0 +1,118 @@ -+package com.infernalsuite.aswm; -+ -+import com.flowpowered.nbt.CompoundMap; -+import com.flowpowered.nbt.TagType; -+import com.infernalsuite.aswm.api.utils.NibbleArray; -+import net.minecraft.nbt.*; -+import net.minecraft.world.level.chunk.DataLayer; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+ -+import java.util.ArrayList; -+import java.util.List; -+ -+public class Converter { -+ -+ private static final Logger LOGGER = LogManager.getLogger("SWM Converter"); -+ -+ static DataLayer convertArray(NibbleArray array) { -+ return new DataLayer(array.getBacking()); -+ } -+ -+ public static NibbleArray convertArray(DataLayer array) { -+ if(array == null) { -+ return null; -+ } -+ -+ return new NibbleArray(array.getData()); -+ } -+ -+ public static Tag convertTag(com.flowpowered.nbt.Tag tag) { -+ try { -+ switch(tag.getType()) { -+ case TAG_BYTE: -+ return ByteTag.valueOf(((com.flowpowered.nbt.ByteTag) tag).getValue()); -+ case TAG_SHORT: -+ return ShortTag.valueOf(((com.flowpowered.nbt.ShortTag) tag).getValue()); -+ case TAG_INT: -+ return IntTag.valueOf(((com.flowpowered.nbt.IntTag) tag).getValue()); -+ case TAG_LONG: -+ return LongTag.valueOf(((com.flowpowered.nbt.LongTag) tag).getValue()); -+ case TAG_FLOAT: -+ return FloatTag.valueOf(((com.flowpowered.nbt.FloatTag) tag).getValue()); -+ case TAG_DOUBLE: -+ return DoubleTag.valueOf(((com.flowpowered.nbt.DoubleTag) tag).getValue()); -+ case TAG_BYTE_ARRAY: -+ return new ByteArrayTag(((com.flowpowered.nbt.ByteArrayTag) tag).getValue()); -+ case TAG_STRING: -+ return StringTag.valueOf(((com.flowpowered.nbt.StringTag) tag).getValue()); -+ case TAG_LIST: -+ ListTag list = new ListTag(); -+ ((com.flowpowered.nbt.ListTag) tag).getValue().stream().map(Converter::convertTag).forEach(list::add); -+ -+ return list; -+ case TAG_COMPOUND: -+ CompoundTag compound = new CompoundTag(); -+ -+ ((com.flowpowered.nbt.CompoundTag) tag).getValue().forEach((key, value) -> compound.put(key, convertTag(value))); -+ return compound; -+ case TAG_INT_ARRAY: -+ return new IntArrayTag(((com.flowpowered.nbt.IntArrayTag) tag).getValue()); -+ case TAG_LONG_ARRAY: -+ return new LongArrayTag(((com.flowpowered.nbt.LongArrayTag) tag).getValue()); -+ default: -+ throw new IllegalArgumentException("Invalid tag type " + tag.getType().name()); -+ } -+ } catch(Exception ex) { -+ LOGGER.error("Failed to convert NBT object:"); -+ LOGGER.error(tag.toString()); -+ -+ throw ex; -+ } -+ } -+ -+ public static com.flowpowered.nbt.Tag convertTag(String name, Tag base) { -+ switch(base.getId()) { -+ case Tag.TAG_BYTE: -+ return new com.flowpowered.nbt.ByteTag(name, ((ByteTag) base).getAsByte()); -+ case Tag.TAG_SHORT: -+ return new com.flowpowered.nbt.ShortTag(name, ((ShortTag) base).getAsShort()); -+ case Tag.TAG_INT: -+ return new com.flowpowered.nbt.IntTag(name, ((IntTag) base).getAsInt()); -+ case Tag.TAG_LONG: -+ return new com.flowpowered.nbt.LongTag(name, ((LongTag) base).getAsLong()); -+ case Tag.TAG_FLOAT: -+ return new com.flowpowered.nbt.FloatTag(name, ((FloatTag) base).getAsFloat()); -+ case Tag.TAG_DOUBLE: -+ return new com.flowpowered.nbt.DoubleTag(name, ((DoubleTag) base).getAsDouble()); -+ case Tag.TAG_BYTE_ARRAY: -+ return new com.flowpowered.nbt.ByteArrayTag(name, ((ByteArrayTag) base).getAsByteArray()); -+ case Tag.TAG_STRING: -+ return new com.flowpowered.nbt.StringTag(name, ((StringTag) base).getAsString()); -+ case Tag.TAG_LIST: -+ List list = new ArrayList<>(); -+ ListTag originalList = ((ListTag) base); -+ -+ for(Tag entry : originalList) { -+ list.add(convertTag("", entry)); -+ } -+ -+ return new com.flowpowered.nbt.ListTag(name, TagType.getById(originalList.getElementType()), list); -+ case Tag.TAG_COMPOUND: -+ CompoundTag originalCompound = ((CompoundTag) base); -+ com.flowpowered.nbt.CompoundTag compound = new com.flowpowered.nbt.CompoundTag(name, new CompoundMap()); -+ -+ for(String key : originalCompound.getAllKeys()) { -+ compound.getValue().put(key, convertTag(key, originalCompound.get(key))); -+ } -+ -+ return compound; -+ case Tag.TAG_INT_ARRAY: -+ return new com.flowpowered.nbt.IntArrayTag(name, ((IntArrayTag) base).getAsIntArray()); -+ case Tag.TAG_LONG_ARRAY: -+ return new com.flowpowered.nbt.LongArrayTag(name, ((LongArrayTag) base).getAsLongArray()); -+ default: -+ throw new IllegalArgumentException("Invalid tag type " + base.getId()); -+ } -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/InternalPlugin.java b/src/main/java/com/infernalsuite/aswm/InternalPlugin.java -new file mode 100644 -index 0000000000000000000000000000000000000000..61518ab2b68e7a41500f3c8c8a5ec1230597f0e5 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/InternalPlugin.java -@@ -0,0 +1,28 @@ -+package com.infernalsuite.aswm; -+ -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.Server; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.plugin.PluginLogger; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.logging.LogRecord; -+ -+public class InternalPlugin extends MinecraftInternalPlugin { -+ -+ @Override -+ public @NotNull Server getServer() { -+ return MinecraftServer.getServer().server; -+ } -+ -+ @Override -+ public @NotNull PluginLogger getLogger() { -+ return new PluginLogger(new InternalPlugin()) { -+ @Override -+ public void log(@NotNull LogRecord logRecord) { -+ MinecraftServer.LOGGER.info(logRecord.getMessage()); -+ } -+ }; -+ } -+ -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1affe4c94b490a05184deccc9eb80530f67fd5ea ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -@@ -0,0 +1,101 @@ -+package com.infernalsuite.aswm; -+ -+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; -+import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; -+import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; -+import com.flowpowered.nbt.CompoundTag; -+import com.infernalsuite.aswm.serialization.SlimeWorldReader; -+import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -+import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -+import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeChunkSection; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import net.minecraft.SharedConstants; -+ -+import java.util.ArrayList; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.function.Consumer; -+ -+class SimpleDataFixerConverter implements SlimeWorldReader { -+ -+ @Override -+ public SlimeWorld readFromData(SlimeWorld data) { -+ int newVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); -+ int currentVersion = data.getDataVersion(); -+ // Already fixed -+ if (currentVersion == newVersion) { -+ return data; -+ } -+ -+ Map chunks = new HashMap<>(); -+ for (SlimeChunk chunk : data.getChunkStorage()) { -+ List entities = new ArrayList<>(); -+ List blockEntities = new ArrayList<>(); -+ for (CompoundTag upgradeEntity : chunk.getTileEntities()) { -+ blockEntities.add( -+ convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.TILE_ENTITY.convert(new NBTMapType(tag), currentVersion, newVersion)) -+ ); -+ } -+ for (CompoundTag upgradeEntity : chunk.getEntities()) { -+ entities.add( -+ convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.ENTITY.convert(new NBTMapType(tag), currentVersion, newVersion)) -+ ); -+ } -+ -+ SlimeChunkSection[] sections = new SlimeChunkSection[chunk.getSections().length]; -+ for (int i = 0; i < sections.length; i++) { -+ SlimeChunkSection dataSection = chunk.getSections()[i]; -+ -+ com.flowpowered.nbt.CompoundTag blockStateTag = blockStateTag = convertAndBack(dataSection.getBlockStatesTag(), (tag) -> { -+ WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, new NBTMapType(tag), "palette", currentVersion, newVersion); -+ }); -+ -+ com.flowpowered.nbt.CompoundTag biomeTag = convertAndBack(dataSection.getBiomeTag(), (tag) -> { -+ WalkerUtils.convertList(MCTypeRegistry.BIOME, new NBTMapType(tag), "palette", currentVersion, newVersion); -+ }); -+ -+ sections[i] = new SlimeChunkSectionSkeleton( -+ blockStateTag, -+ biomeTag, -+ dataSection.getBlockLight(), -+ dataSection.getSkyLight() -+ ); -+ -+ chunks.put(new ChunkPos(chunk.getX(), chunk.getZ()), new SlimeChunkSkeleton( -+ chunk.getX(), -+ chunk.getZ(), -+ sections, -+ chunk.getHeightMaps(), -+ blockEntities, -+ entities -+ )); -+ } -+ -+ } -+ -+ return new SkeletonSlimeWorld( -+ data.getName(), -+ data.getLoader(), -+ data.isReadOnly(), -+ chunks, -+ data.getExtraData(), -+ data.getPropertyMap(), -+ newVersion -+ ); -+ } -+ -+ -+ private static com.flowpowered.nbt.CompoundTag convertAndBack(com.flowpowered.nbt.CompoundTag value, Consumer acceptor) { -+ if (value == null) { -+ return null; -+ } -+ -+ net.minecraft.nbt.CompoundTag converted = (net.minecraft.nbt.CompoundTag) Converter.convertTag(value); -+ acceptor.accept(converted); -+ -+ return (com.flowpowered.nbt.CompoundTag) Converter.convertTag(value.getName(), converted); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -new file mode 100644 -index 0000000000000000000000000000000000000000..20f16757ba8b8da525ff17d51d4f7eb660c4d22b ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -0,0 +1,206 @@ -+package com.infernalsuite.aswm; -+ -+import com.infernalsuite.aswm.api.SlimeNMSBridge; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.SlimeWorldInstance; -+import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -+import com.infernalsuite.aswm.level.SlimeBootstrap; -+import com.infernalsuite.aswm.level.SlimeInMemoryWorld; -+import com.infernalsuite.aswm.level.SlimeLevelInstance; -+import com.mojang.serialization.Lifecycle; -+import net.kyori.adventure.util.Services; -+import net.minecraft.SharedConstants; -+import net.minecraft.core.registries.Registries; -+import net.minecraft.resources.ResourceKey; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.dedicated.DedicatedServer; -+import net.minecraft.server.dedicated.DedicatedServerProperties; -+import net.minecraft.world.level.GameRules; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.LevelSettings; -+import net.minecraft.world.level.dimension.LevelStem; -+import net.minecraft.world.level.levelgen.WorldOptions; -+import net.minecraft.world.level.storage.CommandStorage; -+import net.minecraft.world.level.storage.DimensionDataStorage; -+import net.minecraft.world.level.storage.PrimaryLevelData; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+import org.bukkit.Bukkit; -+import org.bukkit.World; -+import org.bukkit.craftbukkit.CraftWorld; -+import org.jetbrains.annotations.Nullable; -+ -+import java.io.IOException; -+import java.util.Locale; -+ -+public class SlimeNMSBridgeImpl implements SlimeNMSBridge { -+ -+ private static final SimpleDataFixerConverter DATA_FIXER_CONVERTER = new SimpleDataFixerConverter(); -+ -+ private static final Logger LOGGER = LogManager.getLogger("SWM"); -+ -+ private SlimeWorld defaultWorld; -+ private SlimeWorld defaultNetherWorld; -+ private SlimeWorld defaultEndWorld; -+ -+ public static SlimeNMSBridgeImpl instance() { -+ return (SlimeNMSBridgeImpl) SlimeNMSBridge.instance(); -+ } -+ -+ @Override -+ public boolean loadOverworldOverride() { -+ if (defaultWorld == null) { -+ return false; -+ } -+ -+ // See MinecraftServer loading logic -+ // Some stuff is needed when loading overworld world -+ SlimeLevelInstance instance = ((SlimeInMemoryWorld) this.loadInstance(defaultWorld, Level.OVERWORLD)).getInstance(); -+ DimensionDataStorage worldpersistentdata = instance.getDataStorage(); -+ instance.getCraftServer().scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(instance.getServer(), instance.getScoreboard()); -+ instance.getServer().commandStorage = new CommandStorage(worldpersistentdata); -+ -+ return true; -+ } -+ -+ @Override -+ public boolean loadNetherOverride() { -+ if (defaultNetherWorld == null) { -+ return false; -+ } -+ -+ this.loadInstance(defaultNetherWorld, Level.NETHER); -+ -+ return true; -+ } -+ -+ @Override -+ public boolean loadEndOverride() { -+ if (defaultEndWorld == null) { -+ return false; -+ } -+ -+ this.loadInstance(defaultEndWorld, Level.END); -+ -+ return true; -+ } -+ -+ @Override -+ public void setDefaultWorlds(SlimeWorld normalWorld, SlimeWorld netherWorld, SlimeWorld endWorld) { -+ if (normalWorld != null) { -+ normalWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.NORMAL.toString().toLowerCase()); -+ defaultWorld = normalWorld; -+ } -+ -+ if (netherWorld != null) { -+ netherWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.NETHER.toString().toLowerCase()); -+ defaultNetherWorld = netherWorld; -+ } -+ -+ if (endWorld != null) { -+ endWorld.getPropertyMap().setValue(SlimeProperties.ENVIRONMENT, World.Environment.THE_END.toString().toLowerCase()); -+ defaultEndWorld = endWorld; -+ } -+ -+ } -+ -+ @Override -+ public SlimeWorldInstance loadInstance(SlimeWorld slimeWorld) { -+ return this.loadInstance(slimeWorld, null); -+ } -+ -+ public SlimeWorldInstance loadInstance(SlimeWorld slimeWorld, @Nullable ResourceKey dimensionOverride) { -+ String worldName = slimeWorld.getName(); -+ -+ if (Bukkit.getWorld(worldName) != null) { -+ throw new IllegalArgumentException("World " + worldName + " already exists! Maybe it's an outdated SlimeWorld object?"); -+ } -+ -+ SlimeLevelInstance server = createCustomWorld(slimeWorld, dimensionOverride); -+ registerWorld(server); -+ return server.getSlimeInstance(); -+ } -+ -+ @Override -+ public SlimeWorldInstance getInstance(World world) { -+ CraftWorld craftWorld = (CraftWorld) world; -+ -+ if (!(craftWorld.getHandle() instanceof SlimeLevelInstance worldServer)) { -+ return null; -+ } -+ -+ return worldServer.getSlimeInstance(); -+ } -+ -+ @Override -+ public SlimeWorld applyDataFixers(SlimeWorld world) { -+ return DATA_FIXER_CONVERTER.readFromData(world); -+ } -+ -+ -+ @Override -+ public int getCurrentVersion() { -+ return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); -+ } -+ -+ public void registerWorld(SlimeLevelInstance server) { -+ MinecraftServer mcServer = MinecraftServer.getServer(); -+ mcServer.initWorld(server, server.serverLevelData, mcServer.getWorldData(), server.serverLevelData.worldGenOptions()); -+ -+ mcServer.addLevel(server); -+ } -+ -+ private SlimeLevelInstance createCustomWorld(SlimeWorld world, @Nullable ResourceKey dimensionOverride) { -+ SlimeBootstrap bootstrap = new SlimeBootstrap(world); -+ String worldName = world.getName(); -+ -+ PrimaryLevelData worldDataServer = createWorldData(world); -+ World.Environment environment = getEnvironment(world); -+ ResourceKey dimension = switch (environment) { -+ case NORMAL -> LevelStem.OVERWORLD; -+ case NETHER -> LevelStem.NETHER; -+ case THE_END -> LevelStem.END; -+ default -> throw new IllegalArgumentException("Unknown dimension supplied"); -+ }; -+ -+ ResourceKey worldKey = dimensionOverride == null ? ResourceKey.create(Registries.DIMENSION, new ResourceLocation(worldName.toLowerCase(Locale.ENGLISH))) : dimensionOverride; -+ LevelStem stem = MinecraftServer.getServer().registries().compositeAccess().registryOrThrow(Registries.LEVEL_STEM).get(dimension); -+ -+ SlimeLevelInstance level; -+ -+ try { -+ level = new SlimeLevelInstance(bootstrap, worldDataServer, worldKey, dimension, stem, environment); -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); // TODO do something better with this? -+ } -+ -+ // level.setReady(true); -+ level.setSpawnSettings(world.getPropertyMap().getValue(SlimeProperties.ALLOW_MONSTERS), world.getPropertyMap().getValue(SlimeProperties.ALLOW_ANIMALS)); -+ -+ return level; -+ } -+ -+ private World.Environment getEnvironment(SlimeWorld world) { -+ return World.Environment.valueOf(world.getPropertyMap().getValue(SlimeProperties.ENVIRONMENT).toUpperCase()); -+ } -+ -+ private PrimaryLevelData createWorldData(SlimeWorld world) { -+ MinecraftServer mcServer = MinecraftServer.getServer(); -+ DedicatedServerProperties serverProps = ((DedicatedServer) mcServer).getProperties(); -+ String worldName = world.getName(); -+ -+ LevelSettings worldsettings = new LevelSettings(worldName, serverProps.gamemode, false, serverProps.difficulty, -+ true, new GameRules(), mcServer.worldLoader.dataConfiguration()); -+ -+ WorldOptions worldoptions = new WorldOptions(0, false, false); -+ -+ PrimaryLevelData data = new PrimaryLevelData(worldsettings, worldoptions, PrimaryLevelData.SpecialWorldProperty.FLAT, Lifecycle.stable()); -+ data.checkName(worldName); -+ data.setModdedInfo(mcServer.getServerModName(), mcServer.getModdedStatus().shouldReportAsModified()); -+ data.setInitialized(true); -+ -+ return data; -+ } -+ -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..41e652b568598926e838e81fdc338e51f8e97ef8 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -0,0 +1,121 @@ -+package com.infernalsuite.aswm.level; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.mojang.logging.LogUtils; -+import io.papermc.paper.chunk.system.poi.PoiChunk; -+import io.papermc.paper.chunk.system.scheduling.ChunkLoadTask; -+import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; -+import io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.ImposterProtoChunk; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.UpgradeData; -+import net.minecraft.world.level.material.Fluid; -+import net.minecraft.world.ticks.LevelChunkTicks; -+import org.slf4j.Logger; -+ -+import java.util.function.Consumer; -+ -+public final class ChunkDataLoadTask implements CommonLoadTask { -+ -+ private static final Logger LOGGER = LogUtils.getClassLogger(); -+ -+ private final ChunkTaskScheduler scheduler; -+ private final ServerLevel world; -+ private final int chunkX; -+ private final int chunkZ; -+ private Consumer> onRun; -+ -+ private PrioritisedExecutor.PrioritisedTask task; -+ -+ private final ChunkLoadTask chunkLoadTask; -+ -+ protected ChunkDataLoadTask(ChunkLoadTask chunkLoadTask, final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, -+ final int chunkZ, final PrioritisedExecutor.Priority priority, final Consumer> onRun) { -+ this.chunkLoadTask = chunkLoadTask; -+ this.scheduler = scheduler; -+ this.world = world; -+ this.chunkX = chunkX; -+ this.chunkZ = chunkZ; -+ this.onRun = onRun; -+ -+ this.task = this.scheduler.createChunkTask(this.chunkX, this.chunkZ, () -> { -+ try { -+ SlimeChunk chunk = ((SlimeLevelInstance) this.world).slimeInstance.getChunk(this.chunkX, this.chunkZ); -+ this.onRun.accept(new GenericDataLoadTask.TaskResult<>(runOnMain(chunk), null)); -+ } catch (Throwable e) { -+ LOGGER.error("ERROR", e); -+ this.onRun.accept(new GenericDataLoadTask.TaskResult<>(null, e)); -+ } -+ }, priority); -+ } -+ -+ private ChunkAccess getEmptyChunk() { -+ LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); -+ LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); -+ -+ return new ImposterProtoChunk(new LevelChunk(this.world, new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, -+ 0L, null, null, null), true); -+ } -+ -+ protected ChunkAccess runOnMain(final SlimeChunk data) { -+ final PoiChunk poiChunk = this.chunkLoadTask.chunkHolder.getPoiChunk(); -+ if (poiChunk == null) { -+ LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString()); -+ } else { -+ poiChunk.load(); -+ } -+ -+ // have tasks to run (at this point, it's just the POI consistency checking) -+ try { -+ // if (data.tasks != null) { -+ // for (int i = 0, len = data.tasks.size(); i < len; i) { -+ // data.tasks.poll().run(); -+ // } -+ // } -+ -+ LevelChunk chunk = this.world.slimeInstance.promote(chunkX, chunkZ, data); -+ -+ return new ImposterProtoChunk(chunk, false); -+ } catch (final ThreadDeath death) { -+ throw death; -+ } catch (final Throwable thr2) { -+ LOGGER.error("Failed to parse main tasks for task " + this.toString() + ", chunk data will be lost", thr2); -+ return this.getEmptyChunk(); -+ } -+ } -+ -+ @Override -+ public PrioritisedExecutor.Priority getPriority() { -+ return this.task.getPriority(); -+ } -+ -+ @Override -+ public void setPriority(PrioritisedExecutor.Priority priority) { -+ this.task.setPriority(priority); -+ } -+ -+ @Override -+ public void raisePriority(PrioritisedExecutor.Priority priority) { -+ this.task.raisePriority(priority); -+ } -+ -+ @Override -+ public void lowerPriority(PrioritisedExecutor.Priority priority) { -+ this.task.lowerPriority(priority); -+ } -+ -+ @Override -+ public boolean cancel() { -+ return this.task.cancel(); -+ } -+ -+ public boolean schedule(boolean schedule) { -+ this.scheduler.scheduleChunkTask(chunkX, chunkZ, this.task::execute); -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fc6e46972bcc77134ed718c8c157ec3893d4bcdf ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java -@@ -0,0 +1,18 @@ -+package com.infernalsuite.aswm.level; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+ -+public interface CommonLoadTask { -+ -+ boolean schedule(boolean schedule); -+ -+ PrioritisedExecutor.Priority getPriority(); -+ -+ boolean cancel(); -+ -+ void lowerPriority(PrioritisedExecutor.Priority priority); -+ -+ void raisePriority(PrioritisedExecutor.Priority priority); -+ -+ void setPriority(PrioritisedExecutor.Priority priority); -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java b/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c0e47f25e9be33da374dc737c96d8d3c2bb1cd0f ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java -@@ -0,0 +1,59 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -+import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import io.papermc.paper.world.ChunkEntitySlices; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.LevelChunkSection; -+ -+public class FastChunkPruner { -+ -+ public static boolean canBePruned(SlimeWorld world, LevelChunk chunk) { -+ // Kenox -+ // It's not safe to assume that the chunk can be pruned -+ // if there isn't a loaded chunk there -+ if (chunk == null || chunk.getChunkHolder() == null) { -+ return false; -+ } -+ -+ SlimePropertyMap propertyMap = world.getPropertyMap(); -+ if (propertyMap.getValue(SlimeProperties.SHOULD_LIMIT_SAVE)) { -+ int minX = propertyMap.getValue(SlimeProperties.SAVE_MIN_X); -+ int maxX = propertyMap.getValue(SlimeProperties.SAVE_MAX_X); -+ -+ int minZ = propertyMap.getValue(SlimeProperties.SAVE_MIN_Z); -+ int maxZ = propertyMap.getValue(SlimeProperties.SAVE_MAX_Z); -+ -+ int chunkX = chunk.locX; -+ int chunkZ = chunk.locZ; -+ -+ if (chunkX < minX || chunkX > maxX) { -+ return true; -+ } -+ -+ if (chunkZ < minZ || chunkZ > maxZ) { -+ return true; -+ } -+ } -+ -+ String pruningSetting = world.getPropertyMap().getValue(SlimeProperties.CHUNK_PRUNING); -+ if (pruningSetting.equals("aggressive")) { -+ ChunkEntitySlices slices = chunk.getChunkHolder().getEntityChunk(); -+ -+ return chunk.blockEntities.isEmpty() && (slices == null || slices.isEmpty()) && areSectionsEmpty(chunk); -+ } -+ -+ return false; -+ } -+ -+ private static boolean areSectionsEmpty(LevelChunk chunk) { -+ for (LevelChunkSection section : chunk.getSections()) { -+ if (!section.hasOnlyAir()) { -+ return false; -+ } -+ } -+ -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f1db2fe121bb3aabfad727a8133b645524b8f19a ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -0,0 +1,203 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.flowpowered.nbt.CompoundMap; -+import com.flowpowered.nbt.CompoundTag; -+import com.flowpowered.nbt.LongArrayTag; -+import com.google.common.collect.Lists; -+import com.infernalsuite.aswm.Converter; -+import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; -+import com.infernalsuite.aswm.api.utils.NibbleArray; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeChunkSection; -+import com.mojang.logging.LogUtils; -+import com.mojang.serialization.Codec; -+import io.papermc.paper.world.ChunkEntitySlices; -+import net.minecraft.core.Holder; -+import net.minecraft.core.Registry; -+import net.minecraft.core.SectionPos; -+import net.minecraft.core.registries.Registries; -+import net.minecraft.nbt.NbtOps; -+import net.minecraft.nbt.Tag; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.level.LightLayer; -+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.PalettedContainerRO; -+import net.minecraft.world.level.chunk.storage.ChunkSerializer; -+import net.minecraft.world.level.levelgen.Heightmap; -+import net.minecraft.world.level.lighting.LevelLightEngine; -+import org.slf4j.Logger; -+ -+import java.util.ArrayList; -+import java.util.List; -+import java.util.Map; -+ -+public class NMSSlimeChunk implements SlimeChunk { -+ private static final Logger LOGGER = LogUtils.getClassLogger(); -+ -+ private static final CompoundTag EMPTY_BLOCK_STATE_PALETTE; -+ private static final CompoundTag EMPTY_BIOME_PALETTE; -+ -+ // Optimized empty section serialization -+ static { -+ { -+ PalettedContainer empty = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); -+ Tag tag = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(false, (error) -> { -+ throw new AssertionError(error); -+ }); -+ -+ EMPTY_BLOCK_STATE_PALETTE = (CompoundTag) Converter.convertTag("", tag); -+ } -+ { -+ Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME); -+ PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); -+ Tag tag = ChunkSerializer.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(false, (error) -> { -+ throw new AssertionError(error); -+ }); -+ -+ EMPTY_BIOME_PALETTE = (CompoundTag) Converter.convertTag("", tag); -+ } -+ } -+ -+ private LevelChunk chunk; -+ -+ public NMSSlimeChunk(LevelChunk chunk) { -+ this.chunk = chunk; -+ } -+ -+ @Override -+ public int getX() { -+ return chunk.getPos().x; -+ } -+ -+ @Override -+ public int getZ() { -+ return chunk.getPos().z; -+ } -+ -+ @Override -+ public SlimeChunkSection[] getSections() { -+ SlimeChunkSection[] sections = new SlimeChunkSection[this.chunk.getSectionsCount()]; -+ LevelLightEngine lightEngine = chunk.getLevel().getChunkSource().getLightEngine(); -+ -+ Registry biomeRegistry = chunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME); -+ -+ // Ignore deprecation, spigot only method -+ Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS)); -+ -+ for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { -+ LevelChunkSection section = chunk.getSections()[sectionId]; -+ // Sections CANNOT be null in 1.18 -+ -+ // Block Light Nibble Array -+ NibbleArray blockLightArray = Converter.convertArray(lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunk.getPos(), sectionId))); -+ -+ // Sky light Nibble Array -+ NibbleArray skyLightArray = Converter.convertArray(lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunk.getPos(), sectionId))); -+ -+ // Tile/Entity Data -+ -+ // Block Data -+ CompoundTag blockStateTag; -+ if (section.hasOnlyAir()) { -+ blockStateTag = EMPTY_BLOCK_STATE_PALETTE; -+ } else { -+ Tag data = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(false, System.err::println); // todo error handling -+ blockStateTag = (CompoundTag) Converter.convertTag("", data); -+ } -+ -+ -+ CompoundTag biomeTag; -+ PalettedContainer> biomes = (PalettedContainer>) section.getBiomes(); -+ if (biomes.data.palette().getSize() == 1 && biomes.data.palette().maybeHas((h) -> h.is(Biomes.PLAINS))) { -+ biomeTag = EMPTY_BIOME_PALETTE; -+ } else { -+ Tag biomeData = codec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(false, System.err::println); // todo error handling -+ biomeTag = (CompoundTag) Converter.convertTag("", biomeData); -+ } -+ -+ sections[sectionId] = new SlimeChunkSectionSkeleton(blockStateTag, biomeTag, blockLightArray, skyLightArray); -+ } -+ -+ return sections; -+ } -+ -+ @Override -+ public CompoundTag getHeightMaps() { -+ // HeightMap -+ CompoundMap heightMaps = new CompoundMap(); -+ -+ for (Map.Entry entry : chunk.heightmaps.entrySet()) { -+ if (!entry.getKey().keepAfterWorldgen()) { -+ continue; -+ } -+ -+ Heightmap.Types type = entry.getKey(); -+ Heightmap map = entry.getValue(); -+ -+ heightMaps.put(type.name(), new LongArrayTag(type.name(), map.getRawData())); -+ } -+ -+ return new CompoundTag("", heightMaps); -+ } -+ -+ @Override -+ public List getTileEntities() { -+ List tileEntities = new ArrayList<>(); -+ -+ for (BlockEntity entity : chunk.blockEntities.values()) { -+ net.minecraft.nbt.CompoundTag entityNbt = entity.saveWithFullMetadata(); -+ tileEntities.add(entityNbt); -+ } -+ -+ return Lists.transform(tileEntities, (compound) -> { -+ return (CompoundTag) Converter.convertTag("", compound); -+ }); -+ } -+ -+ @Override -+ public List getEntities() { -+ List entities = new ArrayList<>(); -+ -+ if(this.chunk == null || this.chunk.getChunkHolder() == null) { -+ return new ArrayList<>(); -+ } -+ -+ ChunkEntitySlices slices = this.chunk.getChunkHolder().getEntityChunk(); -+ if (slices == null) { -+ return new ArrayList<>(); -+ } -+ -+ // Work by -+ for (Entity entity : slices.entities) { -+ net.minecraft.nbt.CompoundTag entityNbt = new net.minecraft.nbt.CompoundTag(); -+ try { -+ if (entity.save(entityNbt)) { -+ entities.add(entityNbt); -+ } -+ } catch (Exception e) { -+ LOGGER.error("Could not save the entity = {}, exception = {}", entity, e); -+ } -+ } -+ -+ return Lists.transform(entities, (compound) -> { -+ return (CompoundTag) Converter.convertTag("", compound); -+ }); -+ } -+ -+ public LevelChunk getChunk() { -+ return chunk; -+ } -+ -+ public void setChunk(LevelChunk chunk) { -+ this.chunk = chunk; -+ } -+ -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -new file mode 100644 -index 0000000000000000000000000000000000000000..9a27369c00345bbb94aa19f77687269dc94c0b0a ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -@@ -0,0 +1,91 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.flowpowered.nbt.CompoundTag; -+import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -+import com.infernalsuite.aswm.api.loaders.SlimeLoader; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import net.minecraft.SharedConstants; -+import net.minecraft.server.level.ChunkHolder; -+import net.minecraft.world.level.chunk.LevelChunk; -+ -+import java.io.IOException; -+import java.util.Collection; -+import java.util.List; -+import java.util.Objects; -+import java.util.stream.Collectors; -+ -+public class NMSSlimeWorld implements SlimeWorld { -+ -+ private final SlimeInMemoryWorld memoryWorld; -+ private final SlimeLevelInstance instance; -+ -+ public NMSSlimeWorld(SlimeInMemoryWorld memoryWorld) { -+ this.instance = memoryWorld.getInstance(); -+ this.memoryWorld = memoryWorld; -+ } -+ -+ @Override -+ public String getName() { -+ return this.instance.getMinecraftWorld().serverLevelData.getLevelName(); -+ } -+ -+ @Override -+ public SlimeLoader getLoader() { -+ return this.instance.slimeInstance.getSaveStrategy(); -+ } -+ -+ @Override -+ public SlimeChunk getChunk(int x, int z) { -+ LevelChunk chunk = this.instance.getChunkIfLoaded(x, z); -+ if (chunk == null) { -+ return null; -+ } -+ -+ return new NMSSlimeChunk(chunk); -+ } -+ -+ @Override -+ public Collection getChunkStorage() { -+ List chunks = io.papermc.paper.chunk.system.ChunkSystem.getVisibleChunkHolders(this.instance); // Paper -+ return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull) -+ .map(NMSSlimeChunk::new) -+ .collect(Collectors.toList()); -+ } -+ -+ @Override -+ public CompoundTag getExtraData() { -+ return this.instance.slimeInstance.getExtraData(); -+ } -+ -+ @Override -+ public Collection getWorldMaps() { -+ return List.of(); -+ } -+ -+ @Override -+ public SlimePropertyMap getPropertyMap() { -+ return this.instance.slimeInstance.getPropertyMap(); -+ } -+ -+ @Override -+ public boolean isReadOnly() { -+ return this.getLoader() == null; -+ } -+ -+ @Override -+ public SlimeWorld clone(String worldName) { -+ return this.memoryWorld.clone(worldName); -+ } -+ -+ @Override -+ public SlimeWorld clone(String worldName, SlimeLoader loader) throws WorldAlreadyExistsException, IOException { -+ return this.memoryWorld.clone(worldName, loader); -+ } -+ -+ @Override -+ public int getDataVersion() { -+ return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b20a037679182e3c4a8bf31f084078f6d7e4ff46 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -@@ -0,0 +1,83 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.flowpowered.nbt.CompoundTag; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeChunkSection; -+ -+import java.util.List; -+ -+public class SafeNmsChunkWrapper implements SlimeChunk { -+ -+ private final NMSSlimeChunk wrapper; -+ private final SlimeChunk safety; -+ -+ public SafeNmsChunkWrapper(NMSSlimeChunk wrapper, SlimeChunk safety) { -+ this.wrapper = wrapper; -+ this.safety = safety; -+ } -+ -+ @Override -+ public int getX() { -+ return this.wrapper.getX(); -+ } -+ -+ @Override -+ public int getZ() { -+ return this.wrapper.getZ(); -+ } -+ -+ @Override -+ public SlimeChunkSection[] getSections() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getSections(); -+ } -+ -+ return this.wrapper.getSections(); -+ } -+ -+ @Override -+ public CompoundTag getHeightMaps() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getHeightMaps(); -+ } -+ -+ return this.wrapper.getHeightMaps(); -+ } -+ -+ @Override -+ public List getTileEntities() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getTileEntities(); -+ } -+ -+ return this.wrapper.getTileEntities(); -+ } -+ -+ @Override -+ public List getEntities() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getEntities(); -+ } -+ -+ return this.wrapper.getEntities(); -+ } -+ -+ /* -+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; -+ } -+ -+ public NMSSlimeChunk getWrapper() { -+ return wrapper; -+ } -+ -+ public SlimeChunk getSafety() { -+ return safety; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeBootstrap.java b/src/main/java/com/infernalsuite/aswm/level/SlimeBootstrap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8853088c5c6306511716bbffac9bf73c633b61bb ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeBootstrap.java -@@ -0,0 +1,8 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+ -+public record SlimeBootstrap( -+ SlimeWorld initial -+) { -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..91a7f41db47c7df3ecc301e0827a1d07305f604e ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -0,0 +1,164 @@ -+package com.infernalsuite.aswm.level; -+ -+import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; -+import com.flowpowered.nbt.CompoundMap; -+import com.flowpowered.nbt.CompoundTag; -+import com.flowpowered.nbt.LongArrayTag; -+import com.infernalsuite.aswm.Converter; -+import com.infernalsuite.aswm.api.utils.NibbleArray; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeChunkSection; -+import com.mojang.serialization.Codec; -+import com.mojang.serialization.DataResult; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Holder; -+import net.minecraft.core.Registry; -+import net.minecraft.core.registries.Registries; -+import net.minecraft.nbt.NbtOps; -+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.storage.ChunkSerializer; -+import net.minecraft.world.level.levelgen.Heightmap; -+import net.minecraft.world.level.material.Fluid; -+import net.minecraft.world.ticks.LevelChunkTicks; -+ -+import java.util.EnumSet; -+import java.util.List; -+import java.util.Optional; -+ -+public class SlimeChunkConverter { -+ -+ static SlimeChunkLevel deserializeSlimeChunk(SlimeLevelInstance instance, SlimeChunk chunk) { -+ int x = chunk.getX(); -+ int z = chunk.getZ(); -+ -+ ChunkPos pos = new ChunkPos(x, z); -+ -+ // Chunk sections -+ LevelChunkSection[] sections = new LevelChunkSection[instance.getSectionsCount()]; -+ -+ SWMRNibbleArray[] blockNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(instance); -+ SWMRNibbleArray[] skyNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(instance); -+ instance.getServer().scheduleOnMain(() -> { -+ instance.getLightEngine().retainData(pos, true); -+ }); -+ -+ Registry biomeRegistry = instance.registryAccess().registryOrThrow(Registries.BIOME); -+ // Ignore deprecated method -+ -+ Codec>> codec = PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS), null); -+ -+ for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { -+ SlimeChunkSection slimeSection = chunk.getSections()[sectionId]; -+ -+ if (slimeSection != null) { -+ NibbleArray blockLight = slimeSection.getBlockLight(); -+ if (blockLight != null) { -+ blockNibbles[sectionId] = new SWMRNibbleArray(blockLight.getBacking()); -+ } -+ -+ NibbleArray skyLight = slimeSection.getSkyLight(); -+ if (skyLight != null) { -+ skyNibbles[sectionId] = new SWMRNibbleArray(skyLight.getBacking()); -+ } -+ -+ PalettedContainer blockPalette; -+ if (slimeSection.getBlockStatesTag() != null) { -+ DataResult> dataresult = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { -+ System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging -+ }); -+ blockPalette = dataresult.getOrThrow(false, System.err::println); // todo proper logging -+ } else { -+ blockPalette = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); -+ } -+ -+ PalettedContainer> biomePalette; -+ -+ if (slimeSection.getBiomeTag() != null) { -+ DataResult>> dataresult = codec.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBiomeTag())).promotePartial((s) -> { -+ System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging -+ }); -+ biomePalette = dataresult.getOrThrow(false, System.err::println); // todo proper logging -+ } else { -+ biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); -+ } -+ -+ if (sectionId < sections.length) { -+ LevelChunkSection section = new LevelChunkSection(blockPalette, biomePalette); -+ sections[sectionId] = section; -+ } -+ } -+ } -+ -+ // Keep the chunk loaded at level 33 to avoid light glitches -+ // Such a high level will let the server not tick the chunk, -+ // but at the same time it won't be completely unloaded from memory -+ // getChunkProvider().addTicket(SWM_TICKET, pos, 33, Unit.INSTANCE); -+ -+ -+ LevelChunk.PostLoadProcessor loadEntities = (nmsChunk) -> { -+ -+ // TODO -+ // Load tile entities -+ List tileEntities = chunk.getTileEntities(); -+ -+ if (tileEntities != null) { -+ for (CompoundTag tag : tileEntities) { -+ Optional type = tag.getStringValue("id"); -+ -+ // Sometimes null tile entities are saved -+ if (type.isPresent()) { -+ BlockPos blockPosition = new BlockPos(tag.getIntValue("x").get(), tag.getIntValue("y").get(), tag.getIntValue("z").get()); -+ BlockState blockData = nmsChunk.getBlockState(blockPosition); -+ BlockEntity entity = BlockEntity.loadStatic(blockPosition, blockData, (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)); -+ -+ if (entity != null) { -+ nmsChunk.setBlockEntity(entity); -+ } -+ } -+ } -+ } -+ }; -+ -+ LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); -+ LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); -+ SlimeChunkLevel nmsChunk = new SlimeChunkLevel(instance, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, 0L, sections, loadEntities, null); -+ -+ // Height Maps -+ EnumSet heightMapTypes = nmsChunk.getStatus().heightmapsAfter(); -+ CompoundMap heightMaps = chunk.getHeightMaps().getValue(); -+ EnumSet unsetHeightMaps = EnumSet.noneOf(Heightmap.Types.class); -+ -+ // Light -+ nmsChunk.setBlockNibbles(blockNibbles); -+ nmsChunk.setSkyNibbles(skyNibbles); -+ -+ for (Heightmap.Types type : heightMapTypes) { -+ String name = type.getSerializedName(); -+ -+ if (heightMaps.containsKey(name)) { -+ LongArrayTag heightMap = (LongArrayTag) heightMaps.get(name); -+ nmsChunk.setHeightmap(type, heightMap.getValue()); -+ } else { -+ unsetHeightMaps.add(type); -+ } -+ } -+ -+ // Don't try to populate heightmaps if there are none. -+ // Does a crazy amount of block lookups -+ if (!unsetHeightMaps.isEmpty()) { -+ Heightmap.primeHeightmaps(nmsChunk, unsetHeightMaps); -+ } -+ -+ return nmsChunk; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b159fc8751e9840b311cc1eda01e496e2dbc5f2e ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -@@ -0,0 +1,27 @@ -+package com.infernalsuite.aswm.level; -+ -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.LevelChunkSection; -+import net.minecraft.world.level.chunk.UpgradeData; -+import net.minecraft.world.level.levelgen.blending.BlendingData; -+import net.minecraft.world.level.material.Fluid; -+import net.minecraft.world.ticks.LevelChunkTicks; -+import org.jetbrains.annotations.Nullable; -+ -+public class SlimeChunkLevel extends LevelChunk { -+ -+ private final SlimeInMemoryWorld inMemoryWorld; -+ -+ public SlimeChunkLevel(SlimeLevelInstance world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks blockTickScheduler, LevelChunkTicks fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) { -+ super(world, pos, upgradeData, blockTickScheduler, fluidTickScheduler, inhabitedTime, sectionArrayInitializer, entityLoader, blendingData); -+ this.inMemoryWorld = world.slimeInstance; -+ } -+ -+ @Override -+ public void unloadCallback() { -+ super.unloadCallback(); -+ this.inMemoryWorld.unload(this); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fd4cfb9cceb4f23265cb3cce7f1f251051bfba92 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -0,0 +1,251 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.flowpowered.nbt.CompoundTag; -+import com.infernalsuite.aswm.ChunkPos; -+import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -+import com.infernalsuite.aswm.api.loaders.SlimeLoader; -+import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; -+import com.infernalsuite.aswm.skeleton.SkeletonCloning; -+import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -+import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.SlimeWorldInstance; -+import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.chunk.UpgradeData; -+import net.minecraft.world.level.material.Fluid; -+import net.minecraft.world.ticks.LevelChunkTicks; -+import org.bukkit.World; -+ -+import java.io.IOException; -+import java.util.ArrayList; -+import java.util.Collection; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+ -+/* -+The concept of this is a bit flawed, since ideally this should be a 1:1 representation of the MC world. -+However, due to the complexity of the chunk system we essentially need to wrap around it. -+This stores slime chunks, and when unloaded, will properly convert it to a normal slime chunk for storage. -+ */ -+public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { -+ -+ private final SlimeLevelInstance instance; -+ private final SlimeWorld liveWorld; -+ -+ private final CompoundTag extra; -+ private final SlimePropertyMap propertyMap; -+ private final SlimeLoader loader; -+ -+ private final Map chunkStorage = new HashMap<>(); -+ private boolean readOnly; -+ // private final Map> entityStorage = new HashMap<>(); -+ -+ public SlimeInMemoryWorld(SlimeBootstrap bootstrap, SlimeLevelInstance instance) { -+ this.instance = instance; -+ this.extra = bootstrap.initial().getExtraData(); -+ this.propertyMap = bootstrap.initial().getPropertyMap(); -+ this.loader = bootstrap.initial().getLoader(); -+ this.readOnly = bootstrap.initial().isReadOnly(); -+ -+ for (SlimeChunk initial : bootstrap.initial().getChunkStorage()) { -+ ChunkPos pos = new ChunkPos(initial.getX(), initial.getZ()); -+ List tags = new ArrayList<>(initial.getEntities()); -+ -+ // this.entityStorage.put(pos, tags); -+ this.chunkStorage.put(pos, initial); -+ } -+ -+ this.liveWorld = new NMSSlimeWorld(this); -+ } -+ -+ @Override -+ public String getName() { -+ return this.instance.getMinecraftWorld().serverLevelData.getLevelName(); -+ } -+ -+ @Override -+ public SlimeLoader getLoader() { -+ return this.loader; -+ } -+ -+ public LevelChunk promote(int x, int z, SlimeChunk chunk) { -+ SlimeChunkLevel levelChunk; -+ if (chunk == null) { -+ net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(x, z); -+ LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); -+ LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); -+ -+ levelChunk = new SlimeChunkLevel(this.instance, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, -+ 0L, null, null, null); -+ -+ chunk = new NMSSlimeChunk(levelChunk); -+ -+ } else { -+ levelChunk = SlimeChunkConverter.deserializeSlimeChunk(this.instance, chunk); -+ chunk = new SafeNmsChunkWrapper(new NMSSlimeChunk(levelChunk), chunk); -+ } -+ this.chunkStorage.put(new ChunkPos(x, z), chunk); -+ -+ return levelChunk; -+ } -+ -+ // Authored by: Kenox -+ // Don't use the NMS live chunk in the chunk map -+ public void unload(LevelChunk providedChunk) { -+ final int x = providedChunk.locX; -+ final int z = providedChunk.locZ; -+ -+ SlimeChunk chunk = new NMSSlimeChunk(providedChunk); -+ -+ if (FastChunkPruner.canBePruned(this.liveWorld, providedChunk)) { -+ this.chunkStorage.remove(new ChunkPos(x, z)); -+ return; -+ } -+ -+ this.chunkStorage.put(new ChunkPos(x, z), -+ new SlimeChunkSkeleton(chunk.getX(), chunk.getZ(), chunk.getSections(), -+ chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities())); -+ } -+ -+ @Override -+ public SlimeChunk getChunk(int x, int z) { -+ return this.chunkStorage.get(new ChunkPos(x, z)); -+ } -+ -+ @Override -+ public Collection getChunkStorage() { -+ return this.chunkStorage.values(); -+ } -+ -+ @Override -+ public World getBukkitWorld() { -+ return this.instance.getWorld(); -+ } -+ -+ @Override -+ public SlimeWorld getSlimeWorldMirror() { -+ return this.liveWorld; -+ } -+ -+ @Override -+ public SlimePropertyMap getPropertyMap() { -+ return this.propertyMap; -+ } -+ -+ @Override -+ public boolean isReadOnly() { -+ return this.getSaveStrategy() == null || this.readOnly; -+ } -+ -+ @Override -+ public SlimeWorld clone(String worldName) { -+ try { -+ return clone(worldName, null); -+ } catch (WorldAlreadyExistsException | IOException ignored) { -+ return null; // Never going to happen -+ } -+ } -+ -+ @Override -+ public SlimeWorld clone(String worldName, SlimeLoader loader) throws WorldAlreadyExistsException, IOException { -+ if (this.getName().equals(worldName)) { -+ throw new IllegalArgumentException("The clone world cannot have the same name as the original world!"); -+ } -+ -+ if (worldName == null) { -+ throw new IllegalArgumentException("The world name cannot be null!"); -+ } -+ if (loader != null) { -+ if (loader.worldExists(worldName)) { -+ throw new WorldAlreadyExistsException(worldName); -+ } -+ } -+ -+ SlimeWorld cloned = SkeletonCloning.fullClone(worldName, this, loader); -+ if (loader != null) { -+ loader.saveWorld(worldName, SlimeSerializer.serialize(cloned)); -+ } -+ -+ return cloned; -+ } -+ -+ @Override -+ public int getDataVersion() { -+ return this.liveWorld.getDataVersion(); -+ } -+ -+ @Override -+ public SlimeLoader getSaveStrategy() { -+ return this.loader; -+ } -+ -+ @Override -+ public CompoundTag getExtraData() { -+ return this.extra; -+ } -+ -+ @Override -+ public Collection getWorldMaps() { -+ return List.of(); -+ } -+ -+ // public Map> getEntityStorage() { -+ // return entityStorage; -+ // } -+ -+ public SlimeWorld getForSerialization() { -+ SlimeWorld world = SkeletonCloning.weakCopy(this); -+ -+ Map cloned = new HashMap<>(); -+ for (Map.Entry entry : this.chunkStorage.entrySet()) { -+ SlimeChunk clonedChunk = entry.getValue(); -+ // NMS "live" chunks need to be converted -+ { -+ LevelChunk chunk = null; -+ if (clonedChunk instanceof SafeNmsChunkWrapper safeNmsChunkWrapper) { -+ if (safeNmsChunkWrapper.shouldDefaultBackToSlimeChunk()) { -+ clonedChunk = safeNmsChunkWrapper.getSafety(); -+ } else { -+ chunk = safeNmsChunkWrapper.getWrapper().getChunk(); -+ } -+ } else if (clonedChunk instanceof NMSSlimeChunk nmsSlimeChunk) { -+ chunk = nmsSlimeChunk.getChunk(); -+ } -+ -+ if (chunk != null) { -+ if (FastChunkPruner.canBePruned(world, chunk)) { -+ continue; -+ } -+ -+ clonedChunk = new SlimeChunkSkeleton( -+ clonedChunk.getX(), -+ clonedChunk.getZ(), -+ clonedChunk.getSections(), -+ clonedChunk.getHeightMaps(), -+ clonedChunk.getTileEntities(), -+ clonedChunk.getEntities() -+ ); -+ } -+ } -+ -+ cloned.put(entry.getKey(), clonedChunk); -+ } -+ -+ return new SkeletonSlimeWorld(world.getName(), -+ world.getLoader(), -+ world.isReadOnly(), -+ cloned, -+ world.getExtraData(), -+ world.getPropertyMap(), -+ world.getDataVersion() -+ ); -+ } -+ -+ public SlimeLevelInstance getInstance() { -+ return instance; -+ } -+} -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4f48b7a1a41aabc78cc9276fbf9f372cb117003f ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java -@@ -0,0 +1,39 @@ -+package com.infernalsuite.aswm.level; -+ -+import com.mojang.serialization.Codec; -+import net.minecraft.core.Holder; -+import net.minecraft.world.level.biome.Biome; -+import net.minecraft.world.level.biome.BiomeSource; -+import net.minecraft.world.level.biome.Climate; -+import net.minecraft.world.level.levelgen.FlatLevelSource; -+import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -+ -+import java.util.List; -+import java.util.Optional; -+import java.util.stream.Stream; -+ -+public class SlimeLevelGenerator extends FlatLevelSource { -+ -+ public SlimeLevelGenerator(Holder biome) { -+ super(new FlatLevelGeneratorSettings(Optional.empty(), biome, List.of()), getSource(biome)); -+ } -+ -+ private static BiomeSource getSource(Holder biome) { -+ return new BiomeSource() { -+ @Override -+ protected Codec codec() { -+ return null; -+ } -+ -+ @Override -+ protected Stream> collectPossibleBiomes() { -+ return Stream.of(biome); -+ } -+ -+ @Override -+ public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler noise) { -+ return biome; -+ } -+ }; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -new file mode 100644 -index 0000000000000000000000000000000000000000..65b475b1292e01c918c1f8144599b5fa78688e97 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -0,0 +1,197 @@ -+package com.infernalsuite.aswm.level; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+import com.infernalsuite.aswm.Converter; -+import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; -+import com.infernalsuite.aswm.api.world.SlimeChunk; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.SlimeWorldInstance; -+import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -+import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import io.papermc.paper.chunk.system.scheduling.ChunkLoadTask; -+import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; -+import io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Holder; -+import net.minecraft.core.registries.Registries; -+import net.minecraft.resources.ResourceKey; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.server.level.TicketType; -+import net.minecraft.util.ProgressListener; -+import net.minecraft.util.Unit; -+import net.minecraft.util.datafix.DataFixers; -+import net.minecraft.world.Difficulty; -+import net.minecraft.world.RandomSequences; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.biome.Biome; -+import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.ChunkGenerator; -+import net.minecraft.world.level.dimension.LevelStem; -+import net.minecraft.world.level.storage.LevelStorageSource; -+import net.minecraft.world.level.storage.PrimaryLevelData; -+import net.minecraft.world.level.validation.DirectoryValidator; -+import net.minecraft.world.level.validation.PathAllowList; -+import org.apache.commons.io.FileUtils; -+import org.bukkit.Bukkit; -+import org.bukkit.event.world.WorldSaveEvent; -+import org.jetbrains.annotations.Nullable; -+ -+import java.io.IOException; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.ArrayList; -+import java.util.Collections; -+import java.util.UUID; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.Executors; -+import java.util.concurrent.Future; -+import java.util.function.Consumer; -+import java.util.logging.Level; -+import java.util.stream.Collectors; -+ -+public class SlimeLevelInstance extends ServerLevel { -+ -+ -+ public static LevelStorageSource CUSTOM_LEVEL_STORAGE; -+ -+ static { -+ try { -+ Path path = Files.createTempDirectory("swm-" + UUID.randomUUID().toString().substring(0, 5)).toAbsolutePath(); -+ DirectoryValidator directoryvalidator = LevelStorageSource.parseValidator(path.resolve("allowed_symlinks.txt")); -+ CUSTOM_LEVEL_STORAGE = new LevelStorageSource(path, path, directoryvalidator, DataFixers.getDataFixer()); -+ -+ FileUtils.forceDeleteOnExit(path.toFile()); -+ -+ } catch (IOException ex) { -+ throw new IllegalStateException("Couldn't create dummy file directory.", ex); -+ } -+ } -+ -+ private static final ExecutorService WORLD_SAVER_SERVICE = Executors.newFixedThreadPool(4, new ThreadFactoryBuilder() -+ .setNameFormat("SWM Pool Thread #%1$d").build()); -+ private static final TicketType SWM_TICKET = TicketType.create("swm-chunk", (a, b) -> 0); -+ -+ private final Object saveLock = new Object(); -+ -+ private boolean ready = false; -+ -+ public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primaryLevelData, -+ ResourceKey worldKey, -+ ResourceKey dimensionKey, LevelStem worldDimension, -+ org.bukkit.World.Environment environment) throws IOException { -+ -+ super(slimeBootstrap, MinecraftServer.getServer(), MinecraftServer.getServer().executor, -+ CUSTOM_LEVEL_STORAGE.createAccess(slimeBootstrap.initial().getName() + UUID.randomUUID(), dimensionKey), -+ primaryLevelData, worldKey, worldDimension, -+ MinecraftServer.getServer().progressListenerFactory.create(11), false, null, 0, -+ Collections.emptyList(), true, environment, null, null); -+ this.slimeInstance = new SlimeInMemoryWorld(slimeBootstrap, this); -+ -+ -+ SlimePropertyMap propertyMap = slimeBootstrap.initial().getPropertyMap(); -+ -+ this.serverLevelData.setDifficulty(Difficulty.valueOf(propertyMap.getValue(SlimeProperties.DIFFICULTY).toUpperCase())); -+ this.serverLevelData.setSpawn(new BlockPos(propertyMap.getValue(SlimeProperties.SPAWN_X), propertyMap.getValue(SlimeProperties.SPAWN_Y), propertyMap.getValue(SlimeProperties.SPAWN_Z)), 0); -+ super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); -+ -+ this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); -+ -+ this.keepSpawnInMemory = false; -+ } -+ -+ @Override -+ public ChunkGenerator getGenerator(SlimeBootstrap slimeBootstrap) { -+ String biomeStr = slimeBootstrap.initial().getPropertyMap().getValue(SlimeProperties.DEFAULT_BIOME); -+ ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, new ResourceLocation(biomeStr)); -+ Holder defaultBiome = MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolder(biomeKey).orElseThrow(); -+ return new SlimeLevelGenerator(defaultBiome); -+ } -+ -+ @Override -+ public void save(@Nullable ProgressListener progressUpdate, boolean forceSave, boolean savingDisabled, boolean close) { -+ try { -+ if (!this.slimeInstance.isReadOnly() && !savingDisabled) { -+ Bukkit.getPluginManager().callEvent(new WorldSaveEvent(getWorld())); -+ -+ //this.getChunkSource().save(forceSave); -+ this.serverLevelData.setWorldBorder(this.getWorldBorder().createSettings()); -+ this.serverLevelData.setCustomBossEvents(MinecraftServer.getServer().getCustomBossEvents().save()); -+ -+ // Update level data -+ net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); -+ net.minecraft.nbt.CompoundTag nbtTagCompound = this.serverLevelData.createTag(MinecraftServer.getServer().registryAccess(), compound); -+ -+ if (MinecraftServer.getServer().isStopped()) { // Make sure the world gets saved before stopping the server by running it from the main thread -+ save().get(); // Async wait for it to finish -+ this.slimeInstance.getLoader().unlockWorld(this.slimeInstance.getName()); // Unlock -+ } else { -+ this.save(); -+ //WORLD_SAVER_SERVICE.execute(this::save); -+ } -+ } -+ } catch (Throwable e) { -+ e.printStackTrace(); -+ } -+ } -+ -+ @Override -+ public void saveIncrementally(boolean doFull) { -+ if (doFull) { -+ this.save(null, false, false); -+ } -+ } -+ -+ private Future save() { -+ synchronized (saveLock) { // Don't want to save the SlimeWorld from multiple threads simultaneously -+ SlimeWorldInstance slimeWorld = this.slimeInstance; -+ Bukkit.getLogger().log(Level.INFO, "Saving world " + this.slimeInstance.getName() + "..."); -+ long start = System.currentTimeMillis(); -+ -+ Bukkit.getLogger().log(Level.INFO, "CONVERTING NMS -> SKELETON"); -+ SlimeWorld world = this.slimeInstance.getForSerialization(); -+ Bukkit.getLogger().log(Level.INFO, "CONVERTED TO SKELETON, PUSHING OFF-THREAD"); -+ return WORLD_SAVER_SERVICE.submit(() -> { -+ try { -+ byte[] serializedWorld = SlimeSerializer.serialize(world); -+ long saveStart = System.currentTimeMillis(); -+ slimeWorld.getSaveStrategy().saveWorld(slimeWorld.getName(), serializedWorld); -+ Bukkit.getLogger().log(Level.INFO, "World " + slimeWorld.getName() + " serialized in " + (saveStart - start) + "ms and saved in " + (System.currentTimeMillis() - saveStart) + "ms."); -+ } catch (IOException | IllegalStateException ex) { -+ ex.printStackTrace(); -+ } -+ }); -+ -+ } -+ } -+ -+ public SlimeWorldInstance getSlimeInstance() { -+ return this.slimeInstance; -+ } -+ -+ public ChunkDataLoadTask getLoadTask(ChunkLoadTask task, ChunkTaskScheduler scheduler, ServerLevel world, int chunkX, int chunkZ, PrioritisedExecutor.Priority priority, Consumer> onRun) { -+ return new ChunkDataLoadTask(task, scheduler, world, chunkX, chunkZ, priority, onRun); -+ } -+ -+ public void loadEntities(int chunkX, int chunkZ) { -+ SlimeChunk slimeChunk = this.slimeInstance.getChunk(chunkX, chunkZ); -+ if (slimeChunk != null) { -+ this.getEntityLookup().addLegacyChunkEntities(new ArrayList<>( -+ EntityType.loadEntitiesRecursive(slimeChunk.getEntities() -+ .stream() -+ .map((tag) -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)) -+ .collect(Collectors.toList()), this) -+ .toList() -+ ), new ChunkPos(chunkX, chunkZ)); -+ } -+ } -+ -+ // @Override -+ // public void unload(LevelChunk chunk) { -+ // this.slimeInstance.unload(chunk); -+ // super.unload(chunk); -+ // } -+} -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java b/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3500005bb09dc484bc333f1e0799613d097a37d3 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java -@@ -0,0 +1,9 @@ -+package com.infernalsuite.aswm.util; -+ -+public class NmsUtil { -+ -+ public static long asLong(int chunkX, int chunkZ) { -+ return (((long) chunkZ) * Integer.MAX_VALUE + ((long) chunkX)); -+ //return (long)chunkX & 4294967295L | ((long)chunkZ & 4294967295L) << 32; -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java -index 790bad0494454ca12ee152e3de6da3da634d9b20..2d6b062c4a3cf682d8e4cdbb7b7c84a7699285f2 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( - private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit"; - - private static final String BRAND_PAPER_NAME = "Paper"; -+ private static final String BRAND_ADVANCED_SLIME_PAPER_NAME = "Paper"; - - private static final String BUILD_DEV = "DEV"; - -@@ -42,9 +43,9 @@ public record ServerBuildInfoImpl( - this( - getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) - .map(Key::key) -- .orElse(BRAND_PAPER_ID), -+ .orElse(BRAND_ADVANCED_SLIME_PAPER_ID), - getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) -- .orElse(BRAND_PAPER_NAME), -+ .orElse(BRAND_ADVANCED_SLIME_PAPER_NAME), - SharedConstants.getCurrentVersion().getId(), - SharedConstants.getCurrentVersion().getName(), - getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) -@@ -61,7 +62,7 @@ public record ServerBuildInfoImpl( - - @Override - public boolean isBrandCompatible(final @NotNull Key brandId) { -- return brandId.equals(this.brandId); -+ return brandId.equals(this.brandId) || brandId.equals(BRAND_PAPER_ID) || brandId.equals(BRAND_ADVANCED_SLIME_PAPER_ID); - } - - @Override -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 663b4ecd520e82aa108d44f2d5c2a20cfc7bc01f..4d9b240534372103268a53712a13027ddd434071 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -288,7 +288,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop persistentStateManagerFactory) { -+ // ASWM START -+ public final com.infernalsuite.aswm.level.SlimeBootstrap bootstrap; -+ public ServerChunkCache(com.infernalsuite.aswm.level.SlimeBootstrap bootstrap, ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory) { // ASWM -+ this.bootstrap = bootstrap; -+ // ASWM end - this.level = world; - this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(world); - this.mainThread = Thread.currentThread(); -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 957cae6ddeba9efe3b55588567ae51e8b86b6a42..d414a0056ab27558440086ab373867bd7e28cdd2 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -595,8 +595,14 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - // Paper end - lag compensation - -- // Add env and gen to constructor, IWorldDataServer -> WorldDataServer - public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { -+ this(null, minecraftserver, executor, convertable_conversionsession, iworlddataserver, resourcekey, worlddimension, worldloadlistener, flag, i, list, flag1, randomsequences, env, gen, biomeProvider); -+ } -+ -+ public com.infernalsuite.aswm.level.SlimeInMemoryWorld slimeInstance; // ASWM -+ -+ // Add env and gen to constructor, IWorldDataServer -> WorldDataServer -+ public ServerLevel(com.infernalsuite.aswm.level.SlimeBootstrap bootstrap, MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { - super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor - this.pvpMode = minecraftserver.isPvpAllowed(); - this.convertable = convertable_conversionsession; -@@ -623,6 +629,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); - } - // CraftBukkit end -+ // ASWM START -+ ChunkGenerator result = this.getGenerator(bootstrap); -+ if (result != null) { -+ chunkgenerator = result; -+ } -+ // ASWM END - boolean flag2 = minecraftserver.forceSynchronousWrites(); - DataFixer datafixer = minecraftserver.getFixerUpper(); - EntityPersistentStorage entitypersistentstorage = new EntityStorage(new SimpleRegionStorage(new RegionStorageInfo(convertable_conversionsession.getLevelId(), resourcekey, "entities"), convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, DataFixTypes.ENTITY_CHUNK), this, minecraftserver); -@@ -633,7 +645,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - int k = this.spigotConfig.simulationDistance; // Spigot - // Paper - rewrite chunk system - -- this.chunkSource = new ServerChunkCache(this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, null, () -> { // Paper - rewrite chunk system -+ this.chunkSource = new ServerChunkCache(bootstrap, this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, null, () -> { // Paper - rewrite chunk system // ASWM - return minecraftserver.overworld().getDataStorage(); - }); - this.chunkSource.getGeneratorState().ensureStructuresGenerated(); -@@ -692,6 +704,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.dragonFight = enderDragonFight; - } - -+ // ASWM START -+ public ChunkGenerator getGenerator(com.infernalsuite.aswm.level.SlimeBootstrap bootstrap) { -+ return null; -+ } -+ // ASWM END -+ - public void setWeatherParameters(int clearDuration, int rainDuration, boolean raining, boolean thundering) { - this.serverLevelData.setClearWeatherTime(clearDuration); - this.serverLevelData.setRainTime(rainDuration); -diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -index 8b84bf2272556ac3321cbf16361d7f48a1cc6873..c8f018ec8c142f0e05fcf346f4b1a31cd887aec6 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -468,7 +468,6 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - this.moonrise$palette = palette; - } - // Paper end - optimise palette reads -- - public void copyFrom(Palette palette, BitStorage storage) { - for (int i = 0; i < storage.getSize(); i++) { - T object = palette.valueFor(storage.get(i)); -diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 774556a62eb240da42e84db4502e2ed43495be17..29e29fb2228f08dff3edd8bf651220b83b16ed7b 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 { - public static String getBukkitVersion() { - String result = "Unknown-Version"; - -- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); -+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/com.infernalsuite.aswm/slimeworldmanager-api/pom.properties"); // ASWM - Properties properties = new Properties(); - - if (stream != null) { -diff --git a/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.SlimeNMSBridge b/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.SlimeNMSBridge -new file mode 100644 -index 0000000000000000000000000000000000000000..d07947f0b42fe491617151e2aa7b0f02ff3ce610 ---- /dev/null -+++ b/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.SlimeNMSBridge -@@ -0,0 +1 @@ -+com.infernalsuite.aswm.SlimeNMSBridgeImpl -\ No newline at end of file diff --git a/patches/server/0002-poi-data-loader.patch b/patches/server/0002-poi-data-loader.patch deleted file mode 100644 index 347cd642c..000000000 --- a/patches/server/0002-poi-data-loader.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Paul19988 -Date: Sun, 11 Jun 2023 17:19:05 +0100 -Subject: [PATCH] poi data loader - - -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java -index bbf9d6c1c9525d97160806819a57be03eca290f1..38ca7bd039bc1b60a63ff68c53e3ec315545a175 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java -@@ -16,7 +16,7 @@ import org.slf4j.Logger; - import org.slf4j.LoggerFactory; - import java.util.Optional; - --public final class PoiChunk { -+public class PoiChunk { - - private static final Logger LOGGER = LoggerFactory.getLogger(PoiChunk.class); - -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -index 41e652b568598926e838e81fdc338e51f8e97ef8..c32d52c68188dc1eb7feeac364cdc4aded1c4574 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -14,6 +14,7 @@ import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ImposterProtoChunk; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.chunk.UpgradeData; -+import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.material.Fluid; - import net.minecraft.world.ticks.LevelChunkTicks; - import org.slf4j.Logger; -@@ -63,13 +64,6 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - } - - protected ChunkAccess runOnMain(final SlimeChunk data) { -- final PoiChunk poiChunk = this.chunkLoadTask.chunkHolder.getPoiChunk(); -- if (poiChunk == null) { -- LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString()); -- } else { -- poiChunk.load(); -- } -- - // have tasks to run (at this point, it's just the POI consistency checking) - try { - // if (data.tasks != null) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 018b24d7611c3fd11536441431abf8f125850129..faf7f4f3bd1fbc91a40e5549a7a5520ea3eaec37 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -369,6 +369,10 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun - if (flag3) { - levellightengine.queueSectionData(LightLayer.SKY, sectionposition, serializablechunkdata_b.skyLight); - } -+ -+ if (world instanceof com.infernalsuite.aswm.level.SlimeLevelInstance) { -+ poiStorage.checkConsistencyWithBlocks(SectionPos.of(chunkPos.getWorldPosition()), achunksection[j]); -+ } - } - } - diff --git a/patches/server/0003-Add-support-for-serializing-deserializing-PDC.patch b/patches/server/0003-Add-support-for-serializing-deserializing-PDC.patch deleted file mode 100644 index 93cc19152..000000000 --- a/patches/server/0003-Add-support-for-serializing-deserializing-PDC.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Sat, 21 Oct 2023 18:11:15 +0200 -Subject: [PATCH] Add support for serializing/deserializing PDC - - -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 20f16757ba8b8da525ff17d51d4f7eb660c4d22b..97a3c0401d1bcf34d8022da5163b8169a66fd5b3 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -11,6 +11,7 @@ import com.mojang.serialization.Lifecycle; - import net.kyori.adventure.util.Services; - import net.minecraft.SharedConstants; - import net.minecraft.core.registries.Registries; -+import net.minecraft.nbt.CompoundTag; - import net.minecraft.resources.ResourceKey; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.server.MinecraftServer; -@@ -178,6 +179,11 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - // level.setReady(true); - level.setSpawnSettings(world.getPropertyMap().getValue(SlimeProperties.ALLOW_MONSTERS), world.getPropertyMap().getValue(SlimeProperties.ALLOW_ANIMALS)); - -+ var nmsExtraData = (CompoundTag) Converter.convertTag(world.getExtraData()); -+ -+ //Attempt to read PDC -+ if (nmsExtraData.get("BukkitValues") != null) level.getWorld().readBukkitValues(nmsExtraData.get("BukkitValues")); -+ - return level; - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index fd4cfb9cceb4f23265cb3cce7f1f251051bfba92..03f0a5c88a6b2d28a5a69649fc1295b51aaf879f 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -2,6 +2,7 @@ package com.infernalsuite.aswm.level; - - import com.flowpowered.nbt.CompoundTag; - import com.infernalsuite.aswm.ChunkPos; -+import com.infernalsuite.aswm.Converter; - import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; - import com.infernalsuite.aswm.api.loaders.SlimeLoader; - import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; -@@ -235,6 +236,17 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - cloned.put(entry.getKey(), clonedChunk); - } - -+ // Serialize Bukkit Values (PDC) -+ -+ var nmsTag = new net.minecraft.nbt.CompoundTag(); -+ -+ instance.getWorld().storeBukkitValues(nmsTag); -+ -+ // Bukkit stores the relevant tag as a tag with the key "BukkitValues" in the tag we supply to it -+ var flowTag = Converter.convertTag("BukkitValues", nmsTag.getCompound("BukkitValues")); -+ -+ world.getExtraData().getValue().put(flowTag); -+ - return new SkeletonSlimeWorld(world.getName(), - world.getLoader(), - world.isReadOnly(), diff --git a/patches/server/0004-Fix-entity-loading.patch b/patches/server/0004-Fix-entity-loading.patch deleted file mode 100644 index 56691b851..000000000 --- a/patches/server/0004-Fix-entity-loading.patch +++ /dev/null @@ -1,139 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Philip Kelley -Date: Thu, 2 Nov 2023 22:56:51 +0000 -Subject: [PATCH] Fix entity loading - - -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -index eafa4e6d55cd0f9314ac0f2b96a7f48fbb5e1a4c..bc0990458df745c92e5bc0530ff35ab992365b3a 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -@@ -117,7 +117,7 @@ public final class NewChunkHolder { - } - - if (!transientChunk) { -- if (entityChunk != null) { -+ if (!(this.world instanceof com.infernalsuite.aswm.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)); -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -index c32d52c68188dc1eb7feeac364cdc4aded1c4574..8485296594c01edcaef271fc37bf2c70932f4986 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -1,12 +1,13 @@ - package com.infernalsuite.aswm.level; - - import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import com.infernalsuite.aswm.Converter; - import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.mojang.logging.LogUtils; --import io.papermc.paper.chunk.system.poi.PoiChunk; - import io.papermc.paper.chunk.system.scheduling.ChunkLoadTask; - import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; - import io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask; -+import net.minecraft.nbt.CompoundTag; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.block.Block; -@@ -14,7 +15,6 @@ import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ImposterProtoChunk; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.chunk.UpgradeData; --import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.material.Fluid; - import net.minecraft.world.ticks.LevelChunkTicks; - import org.slf4j.Logger; -@@ -60,7 +60,7 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); - - return new ImposterProtoChunk(new LevelChunk(this.world, new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, -- 0L, null, null, null), true); -+ 0L, null, chunk -> {}, null), true); - } - - protected ChunkAccess runOnMain(final SlimeChunk data) { -@@ -73,8 +73,12 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - // } - - LevelChunk chunk = this.world.slimeInstance.promote(chunkX, chunkZ, data); -+ ImposterProtoChunk protoChunk = new ImposterProtoChunk(chunk, false); -+ if (data != null) { -+ data.getEntities().stream().map(flowTag -> (CompoundTag) Converter.convertTag(flowTag)).forEach(protoChunk::addEntity); -+ } - -- return new ImposterProtoChunk(chunk, false); -+ return protoChunk; - } catch (final ThreadDeath death) { - throw death; - } catch (final Throwable thr2) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index 91a7f41db47c7df3ecc301e0827a1d07305f604e..27af3aa6ba8ffb100a6b1b50ba584e65c4aee86a 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -15,6 +15,8 @@ import net.minecraft.core.Holder; - import net.minecraft.core.Registry; - import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.NbtOps; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.EntityType; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.biome.Biome; - import net.minecraft.world.level.biome.Biomes; -@@ -106,26 +108,11 @@ public class SlimeChunkConverter { - - - LevelChunk.PostLoadProcessor loadEntities = (nmsChunk) -> { -+ List entities = chunk.getEntities(); - -- // TODO -- // Load tile entities -- List tileEntities = chunk.getTileEntities(); -- -- if (tileEntities != null) { -- for (CompoundTag tag : tileEntities) { -- Optional type = tag.getStringValue("id"); -- -- // Sometimes null tile entities are saved -- if (type.isPresent()) { -- BlockPos blockPosition = new BlockPos(tag.getIntValue("x").get(), tag.getIntValue("y").get(), tag.getIntValue("z").get()); -- BlockState blockData = nmsChunk.getBlockState(blockPosition); -- BlockEntity entity = BlockEntity.loadStatic(blockPosition, blockData, (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)); -- -- if (entity != null) { -- nmsChunk.setBlockEntity(entity); -- } -- } -- } -+ if (entities != null) { -+ net.minecraft.server.level.ChunkMap.postLoadProtoChunk(instance, entities.stream() -+ .map(flowTag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(flowTag)).toList(), nmsChunk.getPos()); - } - }; - -@@ -133,6 +120,25 @@ public class SlimeChunkConverter { - LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); - SlimeChunkLevel nmsChunk = new SlimeChunkLevel(instance, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, 0L, sections, loadEntities, null); - -+ List tileEntities = chunk.getTileEntities(); -+ -+ if (tileEntities != null) { -+ for (CompoundTag tag : tileEntities) { -+ Optional type = tag.getStringValue("id"); -+ -+ // Sometimes null tile entities are saved -+ if (type.isPresent()) { -+ BlockPos blockPosition = new BlockPos(tag.getIntValue("x").get(), tag.getIntValue("y").get(), tag.getIntValue("z").get()); -+ BlockState blockData = nmsChunk.getBlockState(blockPosition); -+ BlockEntity entity = BlockEntity.loadStatic(blockPosition, blockData, (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)); -+ -+ if (entity != null) { -+ nmsChunk.setBlockEntity(entity); -+ } -+ } -+ } -+ } -+ - // Height Maps - EnumSet heightMapTypes = nmsChunk.getStatus().heightmapsAfter(); - CompoundMap heightMaps = chunk.getHeightMaps().getValue(); diff --git a/patches/server/0005-Remove-catch-throwable.patch b/patches/server/0005-Remove-catch-throwable.patch deleted file mode 100644 index 89b3cf28a..000000000 --- a/patches/server/0005-Remove-catch-throwable.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Philip Kelley -Date: Thu, 2 Nov 2023 23:01:26 +0000 -Subject: [PATCH] Remove catch throwable - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -index 8485296594c01edcaef271fc37bf2c70932f4986..f9ac1efca06d8debbb7894160c3e67fd23440ebb 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -48,7 +48,7 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - try { - SlimeChunk chunk = ((SlimeLevelInstance) this.world).slimeInstance.getChunk(this.chunkX, this.chunkZ); - this.onRun.accept(new GenericDataLoadTask.TaskResult<>(runOnMain(chunk), null)); -- } catch (Throwable e) { -+ } catch (final Exception e) { - LOGGER.error("ERROR", e); - this.onRun.accept(new GenericDataLoadTask.TaskResult<>(null, e)); - } -@@ -79,10 +79,8 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - } - - return protoChunk; -- } catch (final ThreadDeath death) { -- throw death; -- } catch (final Throwable thr2) { -- LOGGER.error("Failed to parse main tasks for task " + this.toString() + ", chunk data will be lost", thr2); -+ } catch (final Exception e) { -+ LOGGER.error("Failed to parse main tasks for task {}, chunk data will be lost", this, e); - return this.getEmptyChunk(); - } - } diff --git a/patches/server/0006-Handle-null-data-properly.patch b/patches/server/0006-Handle-null-data-properly.patch deleted file mode 100644 index 3f30631e6..000000000 --- a/patches/server/0006-Handle-null-data-properly.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Philip Kelley -Date: Sat, 4 Nov 2023 20:36:26 +0000 -Subject: [PATCH] Handle null data properly - - -diff --git a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -index 1affe4c94b490a05184deccc9eb80530f67fd5ea..1a4be97069f01a82deadd26a94e86dbebe0e47a0 100644 ---- a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -@@ -45,9 +45,12 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - ); - } - -+ ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); -+ - SlimeChunkSection[] sections = new SlimeChunkSection[chunk.getSections().length]; - for (int i = 0; i < sections.length; i++) { - SlimeChunkSection dataSection = chunk.getSections()[i]; -+ if (dataSection == null) continue; - - com.flowpowered.nbt.CompoundTag blockStateTag = blockStateTag = convertAndBack(dataSection.getBlockStatesTag(), (tag) -> { - WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, new NBTMapType(tag), "palette", currentVersion, newVersion); -@@ -63,17 +66,17 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - dataSection.getBlockLight(), - dataSection.getSkyLight() - ); -- -- chunks.put(new ChunkPos(chunk.getX(), chunk.getZ()), new SlimeChunkSkeleton( -- chunk.getX(), -- chunk.getZ(), -- sections, -- chunk.getHeightMaps(), -- blockEntities, -- entities -- )); - } - -+ chunks.put(chunkPos, new SlimeChunkSkeleton( -+ chunk.getX(), -+ chunk.getX(), -+ sections, -+ chunk.getHeightMaps(), -+ blockEntities, -+ entities -+ )); -+ - } - - return new SkeletonSlimeWorld( diff --git a/patches/server/0007-Make-SlimeWorld-a-PersistentDataHolder.patch b/patches/server/0007-Make-SlimeWorld-a-PersistentDataHolder.patch deleted file mode 100644 index 8db8760b9..000000000 --- a/patches/server/0007-Make-SlimeWorld-a-PersistentDataHolder.patch +++ /dev/null @@ -1,142 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Thu, 26 Oct 2023 14:55:39 +0200 -Subject: [PATCH] Make SlimeWorld a PersistentDataHolder - - -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 97a3c0401d1bcf34d8022da5163b8169a66fd5b3..21c3ea3596a1f954618348afae2b2f7f058393d1 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -1,5 +1,6 @@ - package com.infernalsuite.aswm; - -+import com.flowpowered.nbt.CompoundMap; - import com.infernalsuite.aswm.api.SlimeNMSBridge; - import com.infernalsuite.aswm.api.world.SlimeWorld; - import com.infernalsuite.aswm.api.world.SlimeWorldInstance; -@@ -12,6 +13,7 @@ import net.kyori.adventure.util.Services; - import net.minecraft.SharedConstants; - import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.Tag; - import net.minecraft.resources.ResourceKey; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.server.MinecraftServer; -@@ -30,13 +32,18 @@ import org.apache.logging.log4j.Logger; - import org.bukkit.Bukkit; - import org.bukkit.World; - import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; -+import org.bukkit.persistence.PersistentDataContainer; - import org.jetbrains.annotations.Nullable; - - import java.io.IOException; - import java.util.Locale; -+import java.util.Map; - - public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - -+ private static final CraftPersistentDataTypeRegistry REGISTRY = new CraftPersistentDataTypeRegistry(); - private static final SimpleDataFixerConverter DATA_FIXER_CONVERTER = new SimpleDataFixerConverter(); - - private static final Logger LOGGER = LogManager.getLogger("SWM"); -@@ -49,6 +56,26 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - return (SlimeNMSBridgeImpl) SlimeNMSBridge.instance(); - } - -+ @Override -+ public void extractCraftPDC(PersistentDataContainer source, CompoundMap target) { -+ if (source instanceof CraftPersistentDataContainer craftPDC) { -+ for (Map.Entry entry : craftPDC.getRaw().entrySet()) { -+ target.put(Converter.convertTag(entry.getKey(), entry.getValue())); -+ } -+ } else { -+ throw new IllegalArgumentException("PersistentDataContainer is not a CraftPersistentDataContainer"); -+ } -+ } -+ -+ @Override -+ public PersistentDataContainer extractCompoundMapIntoCraftPDC(CompoundMap source) { -+ var container = new CraftPersistentDataContainer(REGISTRY); -+ for (Map.Entry> entry : source.entrySet()) { -+ container.put(entry.getKey(), Converter.convertTag(entry.getValue())); -+ } -+ return container; -+ } -+ - @Override - public boolean loadOverworldOverride() { - if (defaultWorld == null) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -index 9a27369c00345bbb94aa19f77687269dc94c0b0a..07e542e3f75bf272f53345dc040d90358e7d7b2d 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -@@ -9,6 +9,8 @@ import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; - import net.minecraft.SharedConstants; - import net.minecraft.server.level.ChunkHolder; - import net.minecraft.world.level.chunk.LevelChunk; -+import org.bukkit.persistence.PersistentDataContainer; -+import org.jetbrains.annotations.NotNull; - - import java.io.IOException; - import java.util.Collection; -@@ -88,4 +90,9 @@ public class NMSSlimeWorld implements SlimeWorld { - public int getDataVersion() { - return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); - } -+ -+ @Override -+ public @NotNull PersistentDataContainer getPersistentDataContainer() { -+ return memoryWorld.getPersistentDataContainer(); -+ } - } -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 03f0a5c88a6b2d28a5a69649fc1295b51aaf879f..092dae1f9e68f1c395cd0f8151cd696c0bcdceb0 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -5,6 +5,7 @@ import com.infernalsuite.aswm.ChunkPos; - import com.infernalsuite.aswm.Converter; - import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; - import com.infernalsuite.aswm.api.loaders.SlimeLoader; -+import com.infernalsuite.aswm.pdc.FlowPersistentDataContainer; - import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; - import com.infernalsuite.aswm.skeleton.SkeletonCloning; - import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -@@ -19,6 +20,8 @@ import net.minecraft.world.level.chunk.UpgradeData; - import net.minecraft.world.level.material.Fluid; - import net.minecraft.world.ticks.LevelChunkTicks; - import org.bukkit.World; -+import org.bukkit.persistence.PersistentDataContainer; -+import org.jetbrains.annotations.NotNull; - - import java.io.IOException; - import java.util.ArrayList; -@@ -38,6 +41,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - private final SlimeWorld liveWorld; - - private final CompoundTag extra; -+ private final FlowPersistentDataContainer extraPDC; - private final SlimePropertyMap propertyMap; - private final SlimeLoader loader; - -@@ -60,6 +64,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - this.chunkStorage.put(pos, initial); - } - -+ this.extraPDC = new FlowPersistentDataContainer(extra); - this.liveWorld = new NMSSlimeWorld(this); - } - -@@ -260,4 +265,9 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - public SlimeLevelInstance getInstance() { - return instance; - } -+ -+ @Override -+ public @NotNull PersistentDataContainer getPersistentDataContainer() { -+ return this.extraPDC; -+ } - } diff --git a/patches/server/0008-Add-v12-chunk-pdc-and-extra-nbt.-Fix-double-compress.patch b/patches/server/0008-Add-v12-chunk-pdc-and-extra-nbt.-Fix-double-compress.patch deleted file mode 100644 index f047a3204..000000000 --- a/patches/server/0008-Add-v12-chunk-pdc-and-extra-nbt.-Fix-double-compress.patch +++ /dev/null @@ -1,195 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Tue, 19 Dec 2023 21:47:15 +0100 -Subject: [PATCH] Add v12, chunk pdc and extra nbt. Fix double compression on - tile entities and entities. Fix horrible bug which made chunks go poof. - - -diff --git a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -index 1a4be97069f01a82deadd26a94e86dbebe0e47a0..ca4a80e7b5c73f9669a717adc46b2e9b8c1f48b6 100644 ---- a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -@@ -70,11 +70,12 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - - chunks.put(chunkPos, new SlimeChunkSkeleton( - chunk.getX(), -- chunk.getX(), -+ chunk.getZ(), - sections, - chunk.getHeightMaps(), - blockEntities, -- entities -+ entities, -+ chunk.getExtraData() - )); - - } -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index f1db2fe121bb3aabfad727a8133b645524b8f19a..ad30e83670ca88f09fa7625fc52c224247410623 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -67,9 +67,11 @@ public class NMSSlimeChunk implements SlimeChunk { - } - - private LevelChunk chunk; -+ private CompoundTag extra; - -- public NMSSlimeChunk(LevelChunk chunk) { -+ public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference) { - this.chunk = chunk; -+ this.extra = reference == null ? new CompoundTag("", new CompoundMap()) : reference.getExtraData(); - } - - @Override -@@ -192,6 +194,11 @@ public class NMSSlimeChunk implements SlimeChunk { - }); - } - -+ @Override -+ public CompoundTag getExtraData() { -+ return extra; -+ } -+ - public LevelChunk getChunk() { - return chunk; - } -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -index 07e542e3f75bf272f53345dc040d90358e7d7b2d..004d7bcc5b35c76855787dcf32fe460e73cab38f 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -@@ -45,14 +45,14 @@ public class NMSSlimeWorld implements SlimeWorld { - return null; - } - -- return new NMSSlimeChunk(chunk); -+ return new NMSSlimeChunk(chunk, memoryWorld.getChunk(x, z)); - } - - @Override - public Collection getChunkStorage() { - List chunks = io.papermc.paper.chunk.system.ChunkSystem.getVisibleChunkHolders(this.instance); // Paper - return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull) -- .map(NMSSlimeChunk::new) -+ .map((chunkLevel) -> new NMSSlimeChunk(chunkLevel, memoryWorld.getChunk(chunkLevel.getPos().x, chunkLevel.getPos().z))) // This sucks, is there a better way? - .collect(Collectors.toList()); - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -index b20a037679182e3c4a8bf31f084078f6d7e4ff46..e449b3eebe0d245a2107a6d0018930d32dfc4976 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -@@ -62,6 +62,15 @@ public class SafeNmsChunkWrapper implements SlimeChunk { - return this.wrapper.getEntities(); - } - -+ @Override -+ public CompoundTag getExtraData() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getExtraData(); -+ } -+ -+ return this.wrapper.getExtraData(); -+ } -+ - /* - 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. -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index 27af3aa6ba8ffb100a6b1b50ba584e65c4aee86a..a03c59d2800885e90467812f0088787a85d8cd88 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -165,6 +165,13 @@ public class SlimeChunkConverter { - Heightmap.primeHeightmaps(nmsChunk, unsetHeightMaps); - } - -+ net.minecraft.nbt.CompoundTag nmsExtraData = (net.minecraft.nbt.CompoundTag) Converter.convertTag(chunk.getExtraData()); -+ -+ // Attempt to read PDC from the extra tag -+ if (nmsExtraData.get("ChunkBukkitValues") != null) { -+ nmsChunk.persistentDataContainer.putAll(nmsExtraData.getCompound("ChunkBukkitValues")); -+ } -+ - return nmsChunk; - } - } -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 092dae1f9e68f1c395cd0f8151cd696c0bcdceb0..72a74f0c2cf21c32fa4ffd600cf95eaee003aec6 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -1,6 +1,7 @@ - package com.infernalsuite.aswm.level; - - import com.flowpowered.nbt.CompoundTag; -+import com.flowpowered.nbt.Tag; - import com.infernalsuite.aswm.ChunkPos; - import com.infernalsuite.aswm.Converter; - import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -@@ -88,11 +89,11 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - levelChunk = new SlimeChunkLevel(this.instance, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, - 0L, null, null, null); - -- chunk = new NMSSlimeChunk(levelChunk); -+ chunk = new NMSSlimeChunk(levelChunk, getChunk(x, z)); - - } else { - levelChunk = SlimeChunkConverter.deserializeSlimeChunk(this.instance, chunk); -- chunk = new SafeNmsChunkWrapper(new NMSSlimeChunk(levelChunk), chunk); -+ chunk = new SafeNmsChunkWrapper(new NMSSlimeChunk(levelChunk, chunk), chunk); - } - this.chunkStorage.put(new ChunkPos(x, z), chunk); - -@@ -105,7 +106,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - final int x = providedChunk.locX; - final int z = providedChunk.locZ; - -- SlimeChunk chunk = new NMSSlimeChunk(providedChunk); -+ SlimeChunk chunk = new NMSSlimeChunk(providedChunk, getChunk(x, z)); - - if (FastChunkPruner.canBePruned(this.liveWorld, providedChunk)) { - this.chunkStorage.remove(new ChunkPos(x, z)); -@@ -114,7 +115,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - - this.chunkStorage.put(new ChunkPos(x, z), - new SlimeChunkSkeleton(chunk.getX(), chunk.getZ(), chunk.getSections(), -- chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities())); -+ chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities(), chunk.getExtraData())); - } - - @Override -@@ -227,13 +228,20 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - continue; - } - -+ // Serialize Bukkit Values (PDC) -+ -+ Tag flowTag = Converter.convertTag("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound()); -+ -+ clonedChunk.getExtraData().getValue().put(flowTag); -+ - clonedChunk = new SlimeChunkSkeleton( - clonedChunk.getX(), - clonedChunk.getZ(), - clonedChunk.getSections(), - clonedChunk.getHeightMaps(), - clonedChunk.getTileEntities(), -- clonedChunk.getEntities() -+ clonedChunk.getEntities(), -+ clonedChunk.getExtraData() - ); - } - } -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index 65b475b1292e01c918c1f8144599b5fa78688e97..a525fa1781535d458c5ecb67e261520692c858ac 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -151,9 +151,7 @@ public class SlimeLevelInstance extends ServerLevel { - Bukkit.getLogger().log(Level.INFO, "Saving world " + this.slimeInstance.getName() + "..."); - long start = System.currentTimeMillis(); - -- Bukkit.getLogger().log(Level.INFO, "CONVERTING NMS -> SKELETON"); - SlimeWorld world = this.slimeInstance.getForSerialization(); -- Bukkit.getLogger().log(Level.INFO, "CONVERTED TO SKELETON, PUSHING OFF-THREAD"); - return WORLD_SAVER_SERVICE.submit(() -> { - try { - byte[] serializedWorld = SlimeSerializer.serialize(world); diff --git a/patches/server/0009-Add-migration-from-SRF-1-8.patch b/patches/server/0009-Add-migration-from-SRF-1-8.patch deleted file mode 100644 index 800ce1c81..000000000 --- a/patches/server/0009-Add-migration-from-SRF-1-8.patch +++ /dev/null @@ -1,221 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Sat, 6 Jan 2024 22:23:55 +0100 -Subject: [PATCH] Add migration from SRF 1-8 - - -diff --git a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -index ca4a80e7b5c73f9669a717adc46b2e9b8c1f48b6..ba203e3dc9f6b0be5a92c30808daa0c284616f09 100644 ---- a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -@@ -1,5 +1,6 @@ - package com.infernalsuite.aswm; - -+import ca.spottedleaf.dataconverter.converters.DataConverter; - import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; - import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; - import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; -@@ -30,18 +31,21 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - return data; - } - -+ long encodedNewVersion = DataConverter.encodeVersions(newVersion, Integer.MAX_VALUE); -+ long encodedCurrentVersion = DataConverter.encodeVersions(currentVersion, Integer.MAX_VALUE); -+ - Map chunks = new HashMap<>(); - for (SlimeChunk chunk : data.getChunkStorage()) { - List entities = new ArrayList<>(); - List blockEntities = new ArrayList<>(); - for (CompoundTag upgradeEntity : chunk.getTileEntities()) { - blockEntities.add( -- convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.TILE_ENTITY.convert(new NBTMapType(tag), currentVersion, newVersion)) -+ convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.TILE_ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) - ); - } - for (CompoundTag upgradeEntity : chunk.getEntities()) { - entities.add( -- convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.ENTITY.convert(new NBTMapType(tag), currentVersion, newVersion)) -+ convertAndBack(upgradeEntity, (tag) -> MCTypeRegistry.ENTITY.convert(new NBTMapType(tag), encodedCurrentVersion, encodedNewVersion)) - ); - } - -@@ -53,11 +57,11 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - if (dataSection == null) continue; - - com.flowpowered.nbt.CompoundTag blockStateTag = blockStateTag = convertAndBack(dataSection.getBlockStatesTag(), (tag) -> { -- WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, new NBTMapType(tag), "palette", currentVersion, newVersion); -+ WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, new NBTMapType(tag), "palette", encodedCurrentVersion, encodedNewVersion); - }); - - com.flowpowered.nbt.CompoundTag biomeTag = convertAndBack(dataSection.getBiomeTag(), (tag) -> { -- WalkerUtils.convertList(MCTypeRegistry.BIOME, new NBTMapType(tag), "palette", currentVersion, newVersion); -+ WalkerUtils.convertList(MCTypeRegistry.BIOME, new NBTMapType(tag), "palette", encodedCurrentVersion, encodedNewVersion); - }); - - sections[i] = new SlimeChunkSectionSkeleton( -@@ -75,7 +79,8 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - chunk.getHeightMaps(), - blockEntities, - entities, -- chunk.getExtraData() -+ chunk.getExtraData(), -+ chunk.getUpgradeData() - )); - - } -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 21c3ea3596a1f954618348afae2b2f7f058393d1..18f0f7933c42a7609a3d7bd775b24c372baae175 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -1,5 +1,8 @@ - package com.infernalsuite.aswm; - -+import ca.spottedleaf.dataconverter.converters.DataConverter; -+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; -+import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; - import com.flowpowered.nbt.CompoundMap; - import com.infernalsuite.aswm.api.SlimeNMSBridge; - import com.infernalsuite.aswm.api.world.SlimeWorld; -@@ -9,7 +12,6 @@ import com.infernalsuite.aswm.level.SlimeBootstrap; - import com.infernalsuite.aswm.level.SlimeInMemoryWorld; - import com.infernalsuite.aswm.level.SlimeLevelInstance; - import com.mojang.serialization.Lifecycle; --import net.kyori.adventure.util.Services; - import net.minecraft.SharedConstants; - import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.CompoundTag; -@@ -76,6 +78,20 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - return container; - } - -+ @Override -+ public com.flowpowered.nbt.CompoundTag convertChunkTo1_13(com.flowpowered.nbt.CompoundTag tag) { -+ CompoundTag converted = (CompoundTag) Converter.convertTag(tag); -+ -+ int version = converted.getInt("DataVersion"); -+ -+ long encodedNewVersion = DataConverter.encodeVersions(1624, Integer.MAX_VALUE); -+ long encodedCurrentVersion = DataConverter.encodeVersions(version, Integer.MAX_VALUE); -+ -+ MCTypeRegistry.CHUNK.convert(new NBTMapType(converted), encodedCurrentVersion, encodedNewVersion); -+ -+ return (com.flowpowered.nbt.CompoundTag) Converter.convertTag(tag.getName(), converted); -+ } -+ - @Override - public boolean loadOverworldOverride() { - if (defaultWorld == null) { -@@ -166,7 +182,6 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - return DATA_FIXER_CONVERTER.readFromData(world); - } - -- - @Override - public int getCurrentVersion() { - return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index ad30e83670ca88f09fa7625fc52c224247410623..e183ca25bf67a0519de7a91615fbcfc6ff45a56e 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -26,10 +26,7 @@ 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.PalettedContainerRO; -+import net.minecraft.world.level.chunk.*; - import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.lighting.LevelLightEngine; -@@ -67,11 +64,13 @@ public class NMSSlimeChunk implements SlimeChunk { - } - - private LevelChunk chunk; -- private CompoundTag extra; -+ private final CompoundTag extra; -+ private final CompoundTag upgradeData; - - public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference) { - this.chunk = chunk; - this.extra = reference == null ? new CompoundTag("", new CompoundMap()) : reference.getExtraData(); -+ this.upgradeData = reference == null ? null : reference.getUpgradeData(); - } - - @Override -@@ -199,6 +198,11 @@ public class NMSSlimeChunk implements SlimeChunk { - return extra; - } - -+ @Override -+ public CompoundTag getUpgradeData() { -+ return upgradeData; -+ } -+ - public LevelChunk getChunk() { - return chunk; - } -diff --git a/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -index e449b3eebe0d245a2107a6d0018930d32dfc4976..f5da649f4914319229fdba014e1042abca62f835 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SafeNmsChunkWrapper.java -@@ -71,6 +71,15 @@ public class SafeNmsChunkWrapper implements SlimeChunk { - return this.wrapper.getExtraData(); - } - -+ @Override -+ public CompoundTag getUpgradeData() { -+ if (shouldDefaultBackToSlimeChunk()) { -+ return this.safety.getUpgradeData(); -+ } -+ -+ return this.wrapper.getUpgradeData(); -+ } -+ - /* - 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. -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index a03c59d2800885e90467812f0088787a85d8cd88..75bb1e9355141215c4850f1b57db9434d8212637 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -118,7 +118,14 @@ public class SlimeChunkConverter { - - LevelChunkTicks blockLevelChunkTicks = new LevelChunkTicks<>(); - LevelChunkTicks fluidLevelChunkTicks = new LevelChunkTicks<>(); -- SlimeChunkLevel nmsChunk = new SlimeChunkLevel(instance, pos, UpgradeData.EMPTY, blockLevelChunkTicks, fluidLevelChunkTicks, 0L, sections, loadEntities, null); -+ -+ UpgradeData upgradeData; -+ if (chunk.getUpgradeData() != null) { -+ upgradeData = new UpgradeData((net.minecraft.nbt.CompoundTag) Converter.convertTag(chunk.getUpgradeData()), instance); -+ } else { -+ upgradeData = UpgradeData.EMPTY; -+ } -+ SlimeChunkLevel nmsChunk = new SlimeChunkLevel(instance, pos, upgradeData, blockLevelChunkTicks, fluidLevelChunkTicks, 0L, sections, loadEntities, null); - - List tileEntities = chunk.getTileEntities(); - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 72a74f0c2cf21c32fa4ffd600cf95eaee003aec6..95133e0ff8a8bdfc84c1dd7ff6b2c7ed7ae9a2f9 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -115,7 +115,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - - this.chunkStorage.put(new ChunkPos(x, z), - new SlimeChunkSkeleton(chunk.getX(), chunk.getZ(), chunk.getSections(), -- chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities(), chunk.getExtraData())); -+ chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities(), chunk.getExtraData(), null)); - } - - @Override -@@ -241,7 +241,8 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - clonedChunk.getHeightMaps(), - clonedChunk.getTileEntities(), - clonedChunk.getEntities(), -- clonedChunk.getExtraData() -+ clonedChunk.getExtraData(), -+ clonedChunk.getUpgradeData() - ); - } - } diff --git a/patches/server/0010-Add-default-spawn-yaw-propagate-CraftWorld-setSpawnL.patch b/patches/server/0010-Add-default-spawn-yaw-propagate-CraftWorld-setSpawnL.patch deleted file mode 100644 index d2dd3d0b2..000000000 --- a/patches/server/0010-Add-default-spawn-yaw-propagate-CraftWorld-setSpawnL.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Sat, 18 May 2024 20:34:06 +0200 -Subject: [PATCH] Add default spawn yaw, propagate CraftWorld#setSpawnLocation - to the SlimeProperties. - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index a525fa1781535d458c5ecb67e261520692c858ac..1d5547a74042743e388f77f70b9ebbd37be3f1bc 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -95,7 +95,11 @@ public class SlimeLevelInstance extends ServerLevel { - SlimePropertyMap propertyMap = slimeBootstrap.initial().getPropertyMap(); - - this.serverLevelData.setDifficulty(Difficulty.valueOf(propertyMap.getValue(SlimeProperties.DIFFICULTY).toUpperCase())); -- this.serverLevelData.setSpawn(new BlockPos(propertyMap.getValue(SlimeProperties.SPAWN_X), propertyMap.getValue(SlimeProperties.SPAWN_Y), propertyMap.getValue(SlimeProperties.SPAWN_Z)), 0); -+ this.serverLevelData.setSpawn(new BlockPos( -+ propertyMap.getValue(SlimeProperties.SPAWN_X), -+ propertyMap.getValue(SlimeProperties.SPAWN_Y), -+ propertyMap.getValue(SlimeProperties.SPAWN_Z)), -+ propertyMap.getValue(SlimeProperties.SPAWN_YAW)); - super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); - - this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); -@@ -187,6 +191,17 @@ public class SlimeLevelInstance extends ServerLevel { - } - } - -+ @Override -+ public void setDefaultSpawnPos(BlockPos pos, float angle) { -+ super.setDefaultSpawnPos(pos, angle); -+ -+ SlimePropertyMap propertyMap = this.slimeInstance.getPropertyMap(); -+ propertyMap.setValue(SlimeProperties.SPAWN_X, pos.getX()); -+ propertyMap.setValue(SlimeProperties.SPAWN_Y, pos.getY()); -+ propertyMap.setValue(SlimeProperties.SPAWN_Z, pos.getZ()); -+ propertyMap.setValue(SlimeProperties.SPAWN_YAW, angle); -+ } -+ - // @Override - // public void unload(LevelChunk chunk) { - // this.slimeInstance.unload(chunk); diff --git a/patches/server/0011-Fix-chunks-not-getting-serialized-when-reloaded.patch b/patches/server/0011-Fix-chunks-not-getting-serialized-when-reloaded.patch deleted file mode 100644 index 8684f2c0d..000000000 --- a/patches/server/0011-Fix-chunks-not-getting-serialized-when-reloaded.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Sun, 19 May 2024 19:17:07 +0200 -Subject: [PATCH] Fix chunks not getting serialized when reloaded. - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -index b159fc8751e9840b311cc1eda01e496e2dbc5f2e..2ebabf20c37d2b5c479de5bb241aa334f92a1104 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -@@ -24,4 +24,10 @@ public class SlimeChunkLevel extends LevelChunk { - super.unloadCallback(); - this.inMemoryWorld.unload(this); - } -+ -+ @Override -+ public void loadCallback() { -+ super.loadCallback(); -+ this.inMemoryWorld.ensureChunkMarkedAsLoaded(this); -+ } - } -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 95133e0ff8a8bdfc84c1dd7ff6b2c7ed7ae9a2f9..b54b231e22967eb0b34e6ba9b7ec9cdf64bad87e 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -219,7 +219,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - } else { - chunk = safeNmsChunkWrapper.getWrapper().getChunk(); - } -- } else if (clonedChunk instanceof NMSSlimeChunk nmsSlimeChunk) { -+ } else if (clonedChunk instanceof NMSSlimeChunk nmsSlimeChunk) { - chunk = nmsSlimeChunk.getChunk(); - } - -@@ -279,4 +279,10 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - public @NotNull PersistentDataContainer getPersistentDataContainer() { - return this.extraPDC; - } -+ -+ public void ensureChunkMarkedAsLoaded(SlimeChunkLevel chunk) { -+ if (chunkStorage.get(new ChunkPos(chunk.locX, chunk.locZ)) instanceof SlimeChunkSkeleton skeleton) { -+ chunkStorage.put(new ChunkPos(chunk.locX, chunk.locZ), new NMSSlimeChunk(chunk, skeleton)); -+ } -+ } - } diff --git a/patches/server/0012-Compile-fixes.patch b/patches/server/0012-Compile-fixes.patch deleted file mode 100644 index 75cb8942a..000000000 --- a/patches/server/0012-Compile-fixes.patch +++ /dev/null @@ -1,146 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AverageGithub -Date: Sat, 4 May 2024 20:18:16 +0200 -Subject: [PATCH] Compile fixes - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 41ed3865710e9732f7662e1d3bf287ad9cf80c74..bb915dc69af63cab992a5b7f8654b5390340c89e 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -29,9 +29,6 @@ dependencies { - // ASWM start - implementation(project(":slimeworldmanager-api")) - implementation(project(":core")) -- implementation("io.papermc.paper:paper-mojangapi:1.20.4-R0.1-SNAPSHOT") { -- exclude("io.papermc.paper", "paper-api") -- } - // ASWM end - // Paper start - implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index e183ca25bf67a0519de7a91615fbcfc6ff45a56e..b295d159200e3bf0e48f851ac206b2e09b756bb2 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -46,18 +46,14 @@ public class NMSSlimeChunk implements SlimeChunk { - static { - { - PalettedContainer empty = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); -- Tag tag = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(false, (error) -> { -- throw new AssertionError(error); -- }); -+ Tag tag = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); - - EMPTY_BLOCK_STATE_PALETTE = (CompoundTag) Converter.convertTag("", tag); - } - { - Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME); - PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); -- Tag tag = ChunkSerializer.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(false, (error) -> { -- throw new AssertionError(error); -- }); -+ Tag tag = ChunkSerializer.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); - - EMPTY_BIOME_PALETTE = (CompoundTag) Converter.convertTag("", tag); - } -@@ -110,7 +106,7 @@ public class NMSSlimeChunk implements SlimeChunk { - if (section.hasOnlyAir()) { - blockStateTag = EMPTY_BLOCK_STATE_PALETTE; - } else { -- Tag data = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(false, System.err::println); // todo error handling -+ Tag data = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling - blockStateTag = (CompoundTag) Converter.convertTag("", data); - } - -@@ -120,7 +116,7 @@ public class NMSSlimeChunk implements SlimeChunk { - if (biomes.data.palette().getSize() == 1 && biomes.data.palette().maybeHas((h) -> h.is(Biomes.PLAINS))) { - biomeTag = EMPTY_BIOME_PALETTE; - } else { -- Tag biomeData = codec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(false, System.err::println); // todo error handling -+ Tag biomeData = codec.encodeStart(NbtOps.INSTANCE, section.getBiomes()).getOrThrow(); // todo error handling - biomeTag = (CompoundTag) Converter.convertTag("", biomeData); - } - -@@ -154,7 +150,7 @@ public class NMSSlimeChunk implements SlimeChunk { - List tileEntities = new ArrayList<>(); - - for (BlockEntity entity : chunk.blockEntities.values()) { -- net.minecraft.nbt.CompoundTag entityNbt = entity.saveWithFullMetadata(); -+ net.minecraft.nbt.CompoundTag entityNbt = entity.saveWithFullMetadata(net.minecraft.server.MinecraftServer.getServer().registryAccess()); - tileEntities.add(entityNbt); - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index 75bb1e9355141215c4850f1b57db9434d8212637..003778f3ba9db1f52d7746d3b4b1132e373dd365 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -78,7 +78,7 @@ public class SlimeChunkConverter { - DataResult> dataresult = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { - System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging - }); -- blockPalette = dataresult.getOrThrow(false, System.err::println); // todo proper logging -+ blockPalette = dataresult.getOrThrow(); // todo proper logging - } else { - blockPalette = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); - } -@@ -89,7 +89,7 @@ public class SlimeChunkConverter { - DataResult>> dataresult = codec.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBiomeTag())).promotePartial((s) -> { - System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging - }); -- biomePalette = dataresult.getOrThrow(false, System.err::println); // todo proper logging -+ biomePalette = dataresult.getOrThrow(); // todo proper logging - } else { - biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); - } -@@ -137,7 +137,7 @@ public class SlimeChunkConverter { - if (type.isPresent()) { - BlockPos blockPosition = new BlockPos(tag.getIntValue("x").get(), tag.getIntValue("y").get(), tag.getIntValue("z").get()); - BlockState blockData = nmsChunk.getBlockState(blockPosition); -- BlockEntity entity = BlockEntity.loadStatic(blockPosition, blockData, (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag)); -+ BlockEntity entity = BlockEntity.loadStatic(blockPosition, blockData, (net.minecraft.nbt.CompoundTag) Converter.convertTag(tag), net.minecraft.server.MinecraftServer.getServer().registryAccess()); - - if (entity != null) { - nmsChunk.setBlockEntity(entity); -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java -index 4f48b7a1a41aabc78cc9276fbf9f372cb117003f..aa3ed7005ddfda74b2c3ca1e1dde810c62aa1ce7 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelGenerator.java -@@ -1,6 +1,7 @@ - package com.infernalsuite.aswm.level; - - import com.mojang.serialization.Codec; -+import com.mojang.serialization.MapCodec; - import net.minecraft.core.Holder; - import net.minecraft.world.level.biome.Biome; - import net.minecraft.world.level.biome.BiomeSource; -@@ -21,7 +22,7 @@ public class SlimeLevelGenerator extends FlatLevelSource { - private static BiomeSource getSource(Holder biome) { - return new BiomeSource() { - @Override -- protected Codec codec() { -+ protected MapCodec codec() { - return null; - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index 1d5547a74042743e388f77f70b9ebbd37be3f1bc..b28c2b917accc0249e5df19a65db8820336b7279 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -103,8 +103,6 @@ public class SlimeLevelInstance extends ServerLevel { - super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); - - this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); -- -- this.keepSpawnInMemory = false; - } - - @Override -@@ -123,7 +121,7 @@ public class SlimeLevelInstance extends ServerLevel { - - //this.getChunkSource().save(forceSave); - this.serverLevelData.setWorldBorder(this.getWorldBorder().createSettings()); -- this.serverLevelData.setCustomBossEvents(MinecraftServer.getServer().getCustomBossEvents().save()); -+ this.serverLevelData.setCustomBossEvents(MinecraftServer.getServer().getCustomBossEvents().save(MinecraftServer.getServer().registryAccess())); - - // Update level data - net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); diff --git a/patches/server/0013-Separate-plugin-and-server-rework-API-to-v3.patch b/patches/server/0013-Separate-plugin-and-server-rework-API-to-v3.patch deleted file mode 100644 index 1e5e946cc..000000000 --- a/patches/server/0013-Separate-plugin-and-server-rework-API-to-v3.patch +++ /dev/null @@ -1,758 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Sat, 1 Jun 2024 18:57:39 +0200 -Subject: [PATCH] Separate plugin and server, rework API (to v3) - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 22931927ac8c1fcdc45d44f8d4a898a44831039c..e10d95ce805d03fa43f526dc0a1a1123a67b4f0c 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -29,6 +29,7 @@ dependencies { - // ASWM start - implementation(project(":slimeworldmanager-api")) - implementation(project(":core")) -+ implementation("commons-io:commons-io:2.11.0") - // ASWM end - // Paper start - implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ -diff --git a/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java b/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..719bfb548cfe69cbb726d95b68527bdf45f1eb52 ---- /dev/null -+++ b/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java -@@ -0,0 +1,243 @@ -+package com.infernalsuite.aswm; -+ -+import com.flowpowered.nbt.CompoundMap; -+import com.flowpowered.nbt.CompoundTag; -+import com.infernalsuite.aswm.api.SlimeNMSBridge; -+import com.infernalsuite.aswm.api.AdvancedSlimePaperAPI; -+import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent; -+import com.infernalsuite.aswm.api.exceptions.*; -+import com.infernalsuite.aswm.api.loaders.SlimeLoader; -+import com.infernalsuite.aswm.api.world.SlimeWorld; -+import com.infernalsuite.aswm.api.world.SlimeWorldInstance; -+import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import com.infernalsuite.aswm.level.SlimeLevelInstance; -+import com.infernalsuite.aswm.serialization.anvil.AnvilImportData; -+import com.infernalsuite.aswm.serialization.anvil.AnvilWorldReader; -+import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; -+import com.infernalsuite.aswm.serialization.slime.reader.SlimeWorldReaderRegistry; -+import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; -+import com.infernalsuite.aswm.util.NmsUtil; -+import net.minecraft.server.level.ServerLevel; -+import org.bukkit.Bukkit; -+import org.bukkit.World; -+import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.event.world.WorldLoadEvent; -+import org.slf4j.Logger; -+import org.slf4j.LoggerFactory; -+import org.spigotmc.AsyncCatcher; -+ -+import java.io.File; -+import java.io.IOException; -+import java.util.List; -+import java.util.Map; -+import java.util.Objects; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.ExecutionException; -+import java.util.concurrent.Future; -+ -+public class AdvancedSlimePaper implements AdvancedSlimePaperAPI { -+ -+ private static final Logger LOGGER = LoggerFactory.getLogger(AdvancedSlimePaper.class); -+ private static final SlimeNMSBridge BRIDGE_INSTANCE = SlimeNMSBridge.instance(); -+ -+ private final Map loadedWorlds = new ConcurrentHashMap<>(); -+ -+ static { -+ System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); -+ } -+ -+ public static AdvancedSlimePaper instance() { -+ return (AdvancedSlimePaper) AdvancedSlimePaperAPI.instance(); -+ } -+ -+ @Override -+ public SlimeWorld readWorld(SlimeLoader loader, String worldName, boolean readOnly, SlimePropertyMap propertyMap) throws UnknownWorldException, IOException, CorruptedWorldException, NewerFormatException { -+ Objects.requireNonNull(loader, "Loader cannot be null"); -+ Objects.requireNonNull(worldName, "World name cannot be null"); -+ Objects.requireNonNull(propertyMap, "Properties cannot be null"); -+ -+ long start = System.currentTimeMillis(); -+ -+ LOGGER.info("Reading world {}.", worldName); -+ byte[] serializedWorld = loader.readWorld(worldName); -+ -+ SlimeWorld slimeWorld = SlimeWorldReaderRegistry.readWorld(loader, worldName, serializedWorld, propertyMap, readOnly); -+ LOGGER.info("Applying datafixers for {}.", worldName); -+ SlimeWorld dataFixed = SlimeNMSBridge.instance().applyDataFixers(slimeWorld); -+ -+ // If the dataFixed and slimeWorld are same, then no datafixers were applied -+ if (!readOnly && dataFixed != slimeWorld) -+ loader.saveWorld(worldName, SlimeSerializer.serialize(dataFixed)); // Write dataFixed world back to loader -+ -+ LOGGER.info("World {} read in {}ms.", worldName, System.currentTimeMillis() - start); -+ -+ return dataFixed; -+ } -+ -+ @Override -+ public SlimeWorld loadWorld(SlimeWorld world, boolean callWorldLoadEvent) throws IllegalArgumentException { -+ AsyncCatcher.catchOp("SWM world load"); -+ Objects.requireNonNull(world, "SlimeWorld cannot be null"); -+ -+ if (Bukkit.getWorld(world.getName()) != null) { -+ throw new IllegalArgumentException("World " + world.getName() + " is already loaded"); -+ } -+ -+ LOGGER.info("Loading world {}...", world.getName()); -+ long start = System.currentTimeMillis(); -+ -+ SlimeWorldInstance instance = BRIDGE_INSTANCE.loadInstance(world); -+ SlimeWorld mirror = instance.getSlimeWorldMirror(); -+ -+ Bukkit.getPluginManager().callEvent(new LoadSlimeWorldEvent(mirror)); -+ if (callWorldLoadEvent) { -+ Bukkit.getPluginManager().callEvent(new WorldLoadEvent(instance.getBukkitWorld())); -+ } -+ -+ registerWorld(mirror); -+ -+ LOGGER.info("World {} loaded in {}ms.", world.getName(), System.currentTimeMillis() - start); -+ return mirror; -+ } -+ -+ @Override -+ public boolean worldLoaded(SlimeWorld world) { -+ return loadedWorlds.containsKey(world.getName()); -+ } -+ -+ @Override -+ public void saveWorld(SlimeWorld world) throws IOException { -+ Objects.requireNonNull(world, "SlimeWorld cannot be null"); -+ if (worldLoaded(world)) { -+ Future[] future = new Future[1]; -+ -+ // This is not pretty, but we really need to hop onto the main thread -+ NmsUtil.runSyncAndWait(() -> { -+ World bukkitWorld = Bukkit.getWorld(world.getName()); -+ -+ ServerLevel level = ((CraftWorld) bukkitWorld).getHandle(); -+ if (level instanceof SlimeLevelInstance slimeLevel) { -+ future[0] = slimeLevel.save(); -+ } else { -+ // Shouldn't happen -+ LOGGER.warn("ServerLevel based off of SlimeWorld is not an instance of SlimeLevelInstance. Falling back to default save method."); -+ bukkitWorld.save(); -+ } -+ }); -+ -+ if (future[0] != null) { -+ try { -+ future[0].get(); -+ } catch (InterruptedException exception) { -+ throw new RuntimeException(exception); -+ } catch (ExecutionException e) { -+ if (e.getCause() instanceof IOException ioException) { -+ throw ioException; -+ } else { -+ throw new RuntimeException(e.getCause()); -+ } -+ } -+ } -+ } else { -+ LOGGER.info("Saving unloaded world {}...", world.getName()); -+ Objects.requireNonNull(world.getLoader(), "World loader cannot be null"); -+ long start = System.currentTimeMillis(); -+ -+ byte[] serializedWorld = SlimeSerializer.serialize(world); -+ -+ long saveStart = System.currentTimeMillis(); -+ world.getLoader().saveWorld(world.getName(), serializedWorld); -+ -+ LOGGER.info("World {} serialized in {}ms and saved in {}ms.", world.getName(), saveStart - start, System.currentTimeMillis() - saveStart); -+ } -+ -+ } -+ -+ @Override -+ public SlimeWorld getLoadedWorld(String worldName) { -+ return loadedWorlds.get(worldName); -+ } -+ -+ @Override -+ public List getLoadedWorlds() { -+ return List.copyOf(loadedWorlds.values()); -+ } -+ -+ @Override -+ public SlimeWorld createEmptyWorld(String worldName, boolean readOnly, SlimePropertyMap propertyMap, SlimeLoader loader) { -+ Objects.requireNonNull(worldName, "World name cannot be null"); -+ Objects.requireNonNull(propertyMap, "Properties cannot be null"); -+ -+ return new SkeletonSlimeWorld(worldName, loader, readOnly, Map.of(), new CompoundTag("", new CompoundMap()), propertyMap, BRIDGE_INSTANCE.getCurrentVersion()); -+ } -+ -+ @Override -+ public void migrateWorld(String worldName, SlimeLoader currentLoader, SlimeLoader newLoader) throws IOException, WorldAlreadyExistsException, UnknownWorldException { -+ Objects.requireNonNull(worldName, "World name cannot be null"); -+ Objects.requireNonNull(currentLoader, "Current loader cannot be null"); -+ Objects.requireNonNull(newLoader, "New loader cannot be null"); -+ -+ if (newLoader.worldExists(worldName)) { -+ throw new WorldAlreadyExistsException(worldName); -+ } -+ -+ byte[] serializedWorld = currentLoader.readWorld(worldName); -+ newLoader.saveWorld(worldName, serializedWorld); -+ currentLoader.deleteWorld(worldName); -+ } -+ -+ @Override -+ public SlimeWorld readVanillaWorld(File worldDir, String worldName, SlimeLoader loader) throws InvalidWorldException, WorldLoadedException, WorldTooBigException, IOException, WorldAlreadyExistsException { -+ Objects.requireNonNull(worldDir, "World directory cannot be null"); -+ Objects.requireNonNull(worldName, "World name cannot be null"); -+ -+ if (loader != null && loader.worldExists(worldName)) { -+ throw new WorldAlreadyExistsException(worldName); -+ } -+ -+ World bukkitWorld = Bukkit.getWorld(worldName); -+ -+ if (bukkitWorld != null && BRIDGE_INSTANCE.getInstance(bukkitWorld) == null) { -+ throw new WorldLoadedException(worldDir.getName()); -+ } -+ -+ SlimeWorld world; -+ -+ try { -+ world = AnvilWorldReader.INSTANCE.readFromData(new AnvilImportData(worldDir, worldName, loader)); -+ } catch (RuntimeException e) { -+ if (e.getCause() == null) { -+ throw e; -+ } -+ if (e.getCause() instanceof IOException ioException) { -+ throw ioException; -+ } else if (e.getCause() instanceof InvalidWorldException invalidWorldException) { -+ throw invalidWorldException; -+ } else { -+ throw e; -+ } -+ } -+ -+ // A sanity check to make sure the world is not too big to be serialized -+ try { -+ SlimeSerializer.serialize(world); -+ } catch (IndexOutOfBoundsException ex) { -+ throw new WorldTooBigException(worldDir.getName()); -+ } -+ -+ return world; -+ } -+ -+ /** -+ * Utility method to register a loaded {@link SlimeWorld} with the internal map (for {@link #getLoadedWorld} calls) -+ * -+ * @param world the world to register -+ */ -+ private void registerWorld(SlimeWorld world) { -+ this.loadedWorlds.put(world.getName(), world); -+ } -+ -+ public void onWorldUnload(String name) { -+ this.loadedWorlds.remove(name); -+ } -+} -diff --git a/src/main/java/com/infernalsuite/aswm/InternalPlugin.java b/src/main/java/com/infernalsuite/aswm/InternalPlugin.java -index 61518ab2b68e7a41500f3c8c8a5ec1230597f0e5..875960d0c9fdcbcb3250abc05cfbde48eec0f15a 100644 ---- a/src/main/java/com/infernalsuite/aswm/InternalPlugin.java -+++ b/src/main/java/com/infernalsuite/aswm/InternalPlugin.java -@@ -1,14 +1,36 @@ - package com.infernalsuite.aswm; - -+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; - import net.minecraft.server.MinecraftServer; - import org.bukkit.Server; --import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.configuration.file.FileConfiguration; -+import org.bukkit.generator.BiomeProvider; -+import org.bukkit.generator.ChunkGenerator; -+import org.bukkit.plugin.Plugin; -+import org.bukkit.plugin.PluginBase; -+import org.bukkit.plugin.PluginDescriptionFile; -+import org.bukkit.plugin.PluginLoader; - import org.bukkit.plugin.PluginLogger; - import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; - - import java.util.logging.LogRecord; -+import java.io.File; -+import java.io.InputStream; -+import java.util.List; - --public class InternalPlugin extends MinecraftInternalPlugin { -+public class InternalPlugin extends PluginBase { -+ private boolean enabled = true; -+ -+ private final String pluginName; -+ private PluginDescriptionFile pdf; -+ -+ public InternalPlugin() { -+ this.pluginName = "Minecraft"; -+ pdf = new PluginDescriptionFile(pluginName, "1.0", "nms"); -+ } - - @Override - public @NotNull Server getServer() { -@@ -25,4 +47,113 @@ public class InternalPlugin extends MinecraftInternalPlugin { - }; - } - -+ public void setEnabled(boolean enabled) { -+ this.enabled = enabled; -+ } -+ -+ @Override -+ public File getDataFolder() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public PluginDescriptionFile getDescription() { -+ return pdf; -+ } -+ // Paper start -+ @Override -+ public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() { -+ return pdf; -+ } -+ // Paper end -+ -+ @Override -+ public FileConfiguration getConfig() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public InputStream getResource(String filename) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void saveConfig() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void saveDefaultConfig() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void saveResource(String resourcePath, boolean replace) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void reloadConfig() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public PluginLoader getPluginLoader() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public boolean isEnabled() { -+ return enabled; -+ } -+ -+ @Override -+ public void onDisable() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void onLoad() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void onEnable() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public boolean isNaggable() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public void setNaggable(boolean canNag) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ -+ @Override -+ public LifecycleEventManager getLifecycleManager() { -+ throw new UnsupportedOperationException("Not supported."); -+ } - } -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 18f0f7933c42a7609a3d7bd775b24c372baae175..962c5ebf73261d0ba19b781d9269ef7ffee8b97d 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -48,8 +48,6 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - private static final CraftPersistentDataTypeRegistry REGISTRY = new CraftPersistentDataTypeRegistry(); - private static final SimpleDataFixerConverter DATA_FIXER_CONVERTER = new SimpleDataFixerConverter(); - -- private static final Logger LOGGER = LogManager.getLogger("SWM"); -- - private SlimeWorld defaultWorld; - private SlimeWorld defaultNetherWorld; - private SlimeWorld defaultEndWorld; -@@ -130,6 +128,13 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - return true; - } - -+ /** -+ * Sets the default worlds for the server.
-+ * NOTE: These worlds should be unloaded! -+ * @param normalWorld The default overworld -+ * @param netherWorld The default nether -+ * @param endWorld The default end -+ */ - @Override - public void setDefaultWorlds(SlimeWorld normalWorld, SlimeWorld netherWorld, SlimeWorld endWorld) { - if (normalWorld != null) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index b295d159200e3bf0e48f851ac206b2e09b756bb2..6ef45be4a76c00be5fbfcdb543882fcf41ea6271 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -31,13 +31,14 @@ import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.lighting.LevelLightEngine; - import org.slf4j.Logger; -+import org.slf4j.LoggerFactory; - - import java.util.ArrayList; - import java.util.List; - import java.util.Map; - - public class NMSSlimeChunk implements SlimeChunk { -- private static final Logger LOGGER = LogUtils.getClassLogger(); -+ private static final Logger LOGGER = LoggerFactory.getLogger(NMSSlimeChunk.class); - - private static final CompoundTag EMPTY_BLOCK_STATE_PALETTE; - private static final CompoundTag EMPTY_BIOME_PALETTE; -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -index 004d7bcc5b35c76855787dcf32fe460e73cab38f..56c5db9a22af8ddd1d459bcf1f5b3fc7ca809b72 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeWorld.java -@@ -35,7 +35,7 @@ public class NMSSlimeWorld implements SlimeWorld { - - @Override - public SlimeLoader getLoader() { -- return this.instance.slimeInstance.getSaveStrategy(); -+ return this.instance.slimeInstance.getLoader(); - } - - @Override -@@ -50,7 +50,7 @@ public class NMSSlimeWorld implements SlimeWorld { - - @Override - public Collection getChunkStorage() { -- List chunks = io.papermc.paper.chunk.system.ChunkSystem.getVisibleChunkHolders(this.instance); // Paper -+ List chunks = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.instance); // Paper - return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull) - .map((chunkLevel) -> new NMSSlimeChunk(chunkLevel, memoryWorld.getChunk(chunkLevel.getPos().x, chunkLevel.getPos().z))) // This sucks, is there a better way? - .collect(Collectors.toList()); -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index b54b231e22967eb0b34e6ba9b7ec9cdf64bad87e..114da62698c2897b16042327a4171f785bc58cec 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -145,7 +145,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - - @Override - public boolean isReadOnly() { -- return this.getSaveStrategy() == null || this.readOnly; -+ return this.getLoader() == null || this.readOnly; - } - - @Override -@@ -185,11 +185,6 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - return this.liveWorld.getDataVersion(); - } - -- @Override -- public SlimeLoader getSaveStrategy() { -- return this.loader; -- } -- - @Override - public CompoundTag getExtraData() { - return this.extra; -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index b28c2b917accc0249e5df19a65db8820336b7279..2265f83768c4dc29f67d29730c4be45a194727da 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -24,7 +24,6 @@ import net.minecraft.util.ProgressListener; - import net.minecraft.util.Unit; - import net.minecraft.util.datafix.DataFixers; - import net.minecraft.world.Difficulty; --import net.minecraft.world.RandomSequences; - import net.minecraft.world.entity.EntityType; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.biome.Biome; -@@ -34,11 +33,11 @@ import net.minecraft.world.level.dimension.LevelStem; - import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.level.storage.PrimaryLevelData; - import net.minecraft.world.level.validation.DirectoryValidator; --import net.minecraft.world.level.validation.PathAllowList; - import org.apache.commons.io.FileUtils; - import org.bukkit.Bukkit; - import org.bukkit.event.world.WorldSaveEvent; - import org.jetbrains.annotations.Nullable; -+import org.spigotmc.AsyncCatcher; - - import java.io.IOException; - import java.nio.file.Files; -@@ -46,6 +45,7 @@ import java.nio.file.Path; - import java.util.ArrayList; - import java.util.Collections; - import java.util.UUID; -+import java.util.concurrent.CompletableFuture; - import java.util.concurrent.ExecutorService; - import java.util.concurrent.Executors; - import java.util.concurrent.Future; -@@ -77,8 +77,6 @@ public class SlimeLevelInstance extends ServerLevel { - - private final Object saveLock = new Object(); - -- private boolean ready = false; -- - public SlimeLevelInstance(SlimeBootstrap slimeBootstrap, PrimaryLevelData primaryLevelData, - ResourceKey worldKey, - ResourceKey dimensionKey, LevelStem worldDimension, -@@ -115,8 +113,13 @@ public class SlimeLevelInstance extends ServerLevel { - - @Override - public void save(@Nullable ProgressListener progressUpdate, boolean forceSave, boolean savingDisabled, boolean close) { -+ if (!savingDisabled) save(); -+ } -+ -+ public Future save() { -+ AsyncCatcher.catchOp("SWM world save"); - try { -- if (!this.slimeInstance.isReadOnly() && !savingDisabled) { -+ if (!this.slimeInstance.isReadOnly() && this.slimeInstance.getLoader() != null) { - Bukkit.getPluginManager().callEvent(new WorldSaveEvent(getWorld())); - - //this.getChunkSource().save(forceSave); -@@ -128,16 +131,17 @@ public class SlimeLevelInstance extends ServerLevel { - net.minecraft.nbt.CompoundTag nbtTagCompound = this.serverLevelData.createTag(MinecraftServer.getServer().registryAccess(), compound); - - if (MinecraftServer.getServer().isStopped()) { // Make sure the world gets saved before stopping the server by running it from the main thread -- save().get(); // Async wait for it to finish -- this.slimeInstance.getLoader().unlockWorld(this.slimeInstance.getName()); // Unlock -+ saveInternal().get(); // Async wait for it to finish - } else { -- this.save(); -+ return this.saveInternal(); - //WORLD_SAVER_SERVICE.execute(this::save); - } - } - } catch (Throwable e) { - e.printStackTrace(); -+ return CompletableFuture.failedFuture(e); - } -+ return CompletableFuture.completedFuture(null); - } - - @Override -@@ -147,7 +151,7 @@ public class SlimeLevelInstance extends ServerLevel { - } - } - -- private Future save() { -+ private Future saveInternal() { - synchronized (saveLock) { // Don't want to save the SlimeWorld from multiple threads simultaneously - SlimeWorldInstance slimeWorld = this.slimeInstance; - Bukkit.getLogger().log(Level.INFO, "Saving world " + this.slimeInstance.getName() + "..."); -@@ -158,7 +162,7 @@ public class SlimeLevelInstance extends ServerLevel { - try { - byte[] serializedWorld = SlimeSerializer.serialize(world); - long saveStart = System.currentTimeMillis(); -- slimeWorld.getSaveStrategy().saveWorld(slimeWorld.getName(), serializedWorld); -+ slimeWorld.getLoader().saveWorld(slimeWorld.getName(), serializedWorld); - Bukkit.getLogger().log(Level.INFO, "World " + slimeWorld.getName() + " serialized in " + (saveStart - start) + "ms and saved in " + (System.currentTimeMillis() - saveStart) + "ms."); - } catch (IOException | IllegalStateException ex) { - ex.printStackTrace(); -diff --git a/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java b/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java -index 3500005bb09dc484bc333f1e0799613d097a37d3..4a5a6f208ad91fd861bd6f2b2c008ef14a547d6e 100644 ---- a/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java -+++ b/src/main/java/com/infernalsuite/aswm/util/NmsUtil.java -@@ -1,9 +1,46 @@ - package com.infernalsuite.aswm.util; - -+import com.infernalsuite.aswm.InternalPlugin; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.scheduler.CraftScheduler; -+import org.bukkit.plugin.Plugin; -+ -+import java.util.concurrent.CountDownLatch; -+ - public class NmsUtil { - - public static long asLong(int chunkX, int chunkZ) { - return (((long) chunkZ) * Integer.MAX_VALUE + ((long) chunkX)); - //return (long)chunkX & 4294967295L | ((long)chunkZ & 4294967295L) << 32; - } -+ -+ public static void runSyncAndWait(Runnable runnable) { -+ if (Bukkit.isPrimaryThread()) { -+ runnable.run(); -+ return; -+ } -+ -+ CountDownLatch latch = new CountDownLatch(1); -+ RuntimeException[] runtimeException = new RuntimeException[1]; -+ -+ Bukkit.getScheduler().runTask(new InternalPlugin(), () -> { -+ try { -+ runnable.run(); -+ } catch (RuntimeException e) { -+ runtimeException[0] = e; -+ } finally { -+ latch.countDown(); -+ } -+ }); -+ -+ try { -+ latch.await(); -+ } catch (InterruptedException e) { -+ throw new RuntimeException(e); // Rather propagate the interrupt (and thus prevent further execution) than continue -+ } -+ -+ if (runtimeException[0] != null) { -+ throw runtimeException[0]; -+ } -+ } - } -\ No newline at end of file -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java -index 3e82ea07ca4194844c5528446e2c4a46ff4acee5..1f8bff31ce60f9a1b143e749916fa51cf115f5d7 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java -@@ -64,6 +64,15 @@ class PaperPluginInstanceManager { - } - - public @Nullable Plugin getPlugin(@NotNull String name) { -+ // ASP start - Warn if someone tries to get the old API instance -+ if (name.equals("SlimeWorldManager")) { -+ server.getLogger().warning(""" -+ Hey! It seems like you're trying to access the old SlimeWorldManager API. -+ Since 1.21.0 the API is now provided by the server directly. -+ See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. -+ """); -+ } -+ // ASP end - return this.lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Paper - } - -diff --git a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java -index 26422904751647a061397ce978bba752149003cd..4940083475948eac4fc06446f7ee7e1e8e04d676 100644 ---- a/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java -+++ b/src/main/java/io/papermc/paper/plugin/storage/SimpleProviderStorage.java -@@ -26,6 +26,15 @@ public abstract class SimpleProviderStorage implements ProviderStorage { - - @Override - public void register(PluginProvider provider) { -+ // ASP start - sanity check for old SlimeWorldManager -+ if (provider.getMeta().getName().equals("SlimeWorldManager")) { -+ LOGGER.warn(""" -+ Hey! It looks like you're trying to load the old SlimeWorldManager plugin. -+ ASP no longer works like that, and you should remove the plugin from your server. -+ See the documentation at https://infernalsuite.com/docs/asp/migrating for more information. -+ """); -+ return; -+ } // ASP end - this.providers.add(provider); - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ac8af406180bc680d46e8edc3da0fc2e5211345a..4936c074ad73d92f3b5ed6463126abb5017e221c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -8,6 +8,7 @@ import com.google.common.collect.ImmutableList; - import com.google.common.collect.Iterators; - import com.google.common.collect.Lists; - import com.google.common.collect.MapMaker; -+import com.infernalsuite.aswm.AdvancedSlimePaper; - import com.mojang.authlib.GameProfile; - import com.mojang.brigadier.StringReader; - import com.mojang.brigadier.exceptions.CommandSyntaxException; -@@ -1487,6 +1488,8 @@ public final class CraftServer implements Server { - return false; - } - -+ AdvancedSlimePaper.instance().onWorldUnload(world.getName()); // ASP - Remove unloaded world from map -+ - try { - if (save) { - handle.save(null, true, false); // Paper - Fix saving in unloadWorld -diff --git a/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.AdvancedSlimePaperAPI b/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.AdvancedSlimePaperAPI -new file mode 100644 -index 0000000000000000000000000000000000000000..a01c19e4ddbc844c71ba3a0db6efb2a8082091cf ---- /dev/null -+++ b/src/main/resources/META-INF/services/com.infernalsuite.aswm.api.AdvancedSlimePaperAPI -@@ -0,0 +1 @@ -+com.infernalsuite.aswm.AdvancedSlimePaper -\ No newline at end of file -diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml -index d2a75850af9c6ad2aca66a5f994f1b587d73eac4..3b090baf12044e19150ec66017c76fc48358f1b7 100644 ---- a/src/main/resources/log4j2.xml -+++ b/src/main/resources/log4j2.xml -@@ -6,7 +6,7 @@ - - - -- -+ - - - - - -- -+ - - - -Date: Sun, 16 Jun 2024 03:06:08 +0200 -Subject: [PATCH] replace ChunkPos with long - - -diff --git a/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java b/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java -index 50350820d228e3656c569176aafd2cc534c17c15..280750cd9872210cc9043deea71f76758c2925fd 100644 ---- a/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java -+++ b/src/main/java/com/infernalsuite/aswm/AdvancedSlimePaper.java -@@ -17,6 +17,7 @@ import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; - import com.infernalsuite.aswm.serialization.slime.reader.SlimeWorldReaderRegistry; - import com.infernalsuite.aswm.skeleton.SkeletonSlimeWorld; - import com.infernalsuite.aswm.util.NmsUtil; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import net.minecraft.server.level.ServerLevel; - import org.bukkit.Bukkit; - import org.bukkit.World; -@@ -168,7 +169,7 @@ public class AdvancedSlimePaper implements AdvancedSlimePaperAPI { - Objects.requireNonNull(worldName, "World name cannot be null"); - Objects.requireNonNull(propertyMap, "Properties cannot be null"); - -- return new SkeletonSlimeWorld(worldName, loader, readOnly, Map.of(), new CompoundTag("", new CompoundMap()), propertyMap, BRIDGE_INSTANCE.getCurrentVersion()); -+ return new SkeletonSlimeWorld(worldName, loader, readOnly, new Long2ObjectOpenHashMap<>(0), new CompoundTag("", new CompoundMap()), propertyMap, BRIDGE_INSTANCE.getCurrentVersion()); - } - - @Override -diff --git a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -index ba203e3dc9f6b0be5a92c30808daa0c284616f09..eb441905b43c0f2f7edc104a34f78a18d8f3bedf 100644 ---- a/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/SimpleDataFixerConverter.java -@@ -12,6 +12,8 @@ import com.infernalsuite.aswm.skeleton.SlimeChunkSkeleton; - import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.infernalsuite.aswm.api.world.SlimeChunkSection; - import com.infernalsuite.aswm.api.world.SlimeWorld; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import net.minecraft.SharedConstants; - - import java.util.ArrayList; -@@ -34,7 +36,7 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - long encodedNewVersion = DataConverter.encodeVersions(newVersion, Integer.MAX_VALUE); - long encodedCurrentVersion = DataConverter.encodeVersions(currentVersion, Integer.MAX_VALUE); - -- Map chunks = new HashMap<>(); -+ Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); - for (SlimeChunk chunk : data.getChunkStorage()) { - List entities = new ArrayList<>(); - List blockEntities = new ArrayList<>(); -@@ -49,7 +51,7 @@ class SimpleDataFixerConverter implements SlimeWorldReader { - ); - } - -- ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); -+ long chunkPos = Util.chunkPosition(chunk.getX(), chunk.getZ()); - - SlimeChunkSection[] sections = new SlimeChunkSection[chunk.getSections().length]; - for (int i = 0; i < sections.length; i++) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 114da62698c2897b16042327a4171f785bc58cec..770679851baba2ddb9f8f427f4cd80ea8b32122b 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -2,8 +2,8 @@ package com.infernalsuite.aswm.level; - - import com.flowpowered.nbt.CompoundTag; - import com.flowpowered.nbt.Tag; --import com.infernalsuite.aswm.ChunkPos; - import com.infernalsuite.aswm.Converter; -+import com.infernalsuite.aswm.Util; - import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; - import com.infernalsuite.aswm.api.loaders.SlimeLoader; - import com.infernalsuite.aswm.pdc.FlowPersistentDataContainer; -@@ -15,6 +15,8 @@ import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.infernalsuite.aswm.api.world.SlimeWorld; - import com.infernalsuite.aswm.api.world.SlimeWorldInstance; - import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import net.minecraft.world.level.block.Block; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.chunk.UpgradeData; -@@ -46,7 +48,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - private final SlimePropertyMap propertyMap; - private final SlimeLoader loader; - -- private final Map chunkStorage = new HashMap<>(); -+ private final Long2ObjectMap chunkStorage = new Long2ObjectOpenHashMap<>(); - private boolean readOnly; - // private final Map> entityStorage = new HashMap<>(); - -@@ -58,7 +60,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - this.readOnly = bootstrap.initial().isReadOnly(); - - for (SlimeChunk initial : bootstrap.initial().getChunkStorage()) { -- ChunkPos pos = new ChunkPos(initial.getX(), initial.getZ()); -+ long pos = Util.chunkPosition(initial.getX(), initial.getZ()); - List tags = new ArrayList<>(initial.getEntities()); - - // this.entityStorage.put(pos, tags); -@@ -95,7 +97,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - levelChunk = SlimeChunkConverter.deserializeSlimeChunk(this.instance, chunk); - chunk = new SafeNmsChunkWrapper(new NMSSlimeChunk(levelChunk, chunk), chunk); - } -- this.chunkStorage.put(new ChunkPos(x, z), chunk); -+ this.chunkStorage.put(Util.chunkPosition(x, z), chunk); - - return levelChunk; - } -@@ -109,18 +111,18 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - SlimeChunk chunk = new NMSSlimeChunk(providedChunk, getChunk(x, z)); - - if (FastChunkPruner.canBePruned(this.liveWorld, providedChunk)) { -- this.chunkStorage.remove(new ChunkPos(x, z)); -+ this.chunkStorage.remove(Util.chunkPosition(x, z)); - return; - } - -- this.chunkStorage.put(new ChunkPos(x, z), -+ this.chunkStorage.put(Util.chunkPosition(x, z), - new SlimeChunkSkeleton(chunk.getX(), chunk.getZ(), chunk.getSections(), - chunk.getHeightMaps(), chunk.getTileEntities(), chunk.getEntities(), chunk.getExtraData(), null)); - } - - @Override - public SlimeChunk getChunk(int x, int z) { -- return this.chunkStorage.get(new ChunkPos(x, z)); -+ return this.chunkStorage.get(Util.chunkPosition(x, z)); - } - - @Override -@@ -202,8 +204,8 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - public SlimeWorld getForSerialization() { - SlimeWorld world = SkeletonCloning.weakCopy(this); - -- Map cloned = new HashMap<>(); -- for (Map.Entry entry : this.chunkStorage.entrySet()) { -+ Long2ObjectMap cloned = new Long2ObjectOpenHashMap<>(); -+ for (Long2ObjectMap.Entry entry : this.chunkStorage.long2ObjectEntrySet()) { - SlimeChunk clonedChunk = entry.getValue(); - // NMS "live" chunks need to be converted - { -@@ -242,7 +244,7 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - } - } - -- cloned.put(entry.getKey(), clonedChunk); -+ cloned.put(entry.getLongKey(), clonedChunk); - } - - // Serialize Bukkit Values (PDC) -@@ -276,8 +278,8 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - } - - public void ensureChunkMarkedAsLoaded(SlimeChunkLevel chunk) { -- if (chunkStorage.get(new ChunkPos(chunk.locX, chunk.locZ)) instanceof SlimeChunkSkeleton skeleton) { -- chunkStorage.put(new ChunkPos(chunk.locX, chunk.locZ), new NMSSlimeChunk(chunk, skeleton)); -+ if (chunkStorage.get(Util.chunkPosition(chunk.locX, chunk.locZ)) instanceof SlimeChunkSkeleton skeleton) { -+ chunkStorage.put(Util.chunkPosition(chunk.locX, chunk.locZ), new NMSSlimeChunk(chunk, skeleton)); - } - } - } diff --git a/patches/server/0015-1.21-compatibility.patch b/patches/server/0015-1.21-compatibility.patch deleted file mode 100644 index 87cf21f1e..000000000 --- a/patches/server/0015-1.21-compatibility.patch +++ /dev/null @@ -1,295 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: kyngs -Date: Tue, 9 Jul 2024 02:00:22 +0200 -Subject: [PATCH] 1.21 compatibility - - -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 962c5ebf73261d0ba19b781d9269ef7ffee8b97d..87b6ee19fd165bde2db3a57545c58251dc6bad22 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -212,7 +212,7 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - default -> throw new IllegalArgumentException("Unknown dimension supplied"); - }; - -- ResourceKey worldKey = dimensionOverride == null ? ResourceKey.create(Registries.DIMENSION, new ResourceLocation(worldName.toLowerCase(Locale.ENGLISH))) : dimensionOverride; -+ ResourceKey worldKey = dimensionOverride == null ? ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(worldName.toLowerCase(Locale.ENGLISH))) : dimensionOverride; - LevelStem stem = MinecraftServer.getServer().registries().compositeAccess().registryOrThrow(Registries.LEVEL_STEM).get(dimension); - - SlimeLevelInstance level; -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -index f9ac1efca06d8debbb7894160c3e67fd23440ebb..b5a1f75314aac73fb77e139398017b16acbb8efb 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -1,12 +1,12 @@ - package com.infernalsuite.aswm.level; - - import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+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.infernalsuite.aswm.Converter; - import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.mojang.logging.LogUtils; --import io.papermc.paper.chunk.system.scheduling.ChunkLoadTask; --import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; --import io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.level.ChunkPos; -diff --git a/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java b/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java -index c0e47f25e9be33da374dc737c96d8d3c2bb1cd0f..4e5eb92e0aa40ca02e58f988ec39a6461f63ac2f 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java -+++ b/src/main/java/com/infernalsuite/aswm/level/FastChunkPruner.java -@@ -1,9 +1,9 @@ - package com.infernalsuite.aswm.level; - -+import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; - import com.infernalsuite.aswm.api.world.SlimeWorld; - import com.infernalsuite.aswm.api.world.properties.SlimeProperties; - import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; --import io.papermc.paper.world.ChunkEntitySlices; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.chunk.LevelChunkSection; - -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index 6ef45be4a76c00be5fbfcdb543882fcf41ea6271..9b4b18758d52d66e4abf9e40e49a32428de68b9a 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -1,17 +1,16 @@ - package com.infernalsuite.aswm.level; - -+import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; - import com.flowpowered.nbt.CompoundMap; - import com.flowpowered.nbt.CompoundTag; - import com.flowpowered.nbt.LongArrayTag; - import com.google.common.collect.Lists; - import com.infernalsuite.aswm.Converter; --import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; - import com.infernalsuite.aswm.api.utils.NibbleArray; - import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.infernalsuite.aswm.api.world.SlimeChunkSection; --import com.mojang.logging.LogUtils; -+import com.infernalsuite.aswm.skeleton.SlimeChunkSectionSkeleton; - import com.mojang.serialization.Codec; --import io.papermc.paper.world.ChunkEntitySlices; - import net.minecraft.core.Holder; - import net.minecraft.core.Registry; - import net.minecraft.core.SectionPos; -@@ -26,7 +25,10 @@ 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.*; -+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.storage.ChunkSerializer; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.lighting.LevelLightEngine; -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index 003778f3ba9db1f52d7746d3b4b1132e373dd365..86a5b457bdca63713769d2b708be905d72ff76a3 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -1,6 +1,7 @@ - package com.infernalsuite.aswm.level; - --import ca.spottedleaf.starlight.common.light.SWMRNibbleArray; -+import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray; -+import ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine; - import com.flowpowered.nbt.CompoundMap; - import com.flowpowered.nbt.CompoundTag; - import com.flowpowered.nbt.LongArrayTag; -@@ -15,8 +16,6 @@ import net.minecraft.core.Holder; - import net.minecraft.core.Registry; - import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.NbtOps; --import net.minecraft.world.entity.Entity; --import net.minecraft.world.entity.EntityType; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.biome.Biome; - import net.minecraft.world.level.biome.Biomes; -@@ -28,6 +27,7 @@ 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.ChunkSerializer; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.material.Fluid; -@@ -48,8 +48,8 @@ public class SlimeChunkConverter { - // Chunk sections - LevelChunkSection[] sections = new LevelChunkSection[instance.getSectionsCount()]; - -- SWMRNibbleArray[] blockNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(instance); -- SWMRNibbleArray[] skyNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(instance); -+ SWMRNibbleArray[] blockNibbles = StarLightEngine.getFilledEmptyLight(instance); -+ SWMRNibbleArray[] skyNibbles = StarLightEngine.getFilledEmptyLight(instance); - instance.getServer().scheduleOnMain(() -> { - instance.getLightEngine().retainData(pos, true); - }); -@@ -111,7 +111,7 @@ public class SlimeChunkConverter { - List entities = chunk.getEntities(); - - if (entities != null) { -- net.minecraft.server.level.ChunkMap.postLoadProtoChunk(instance, entities.stream() -+ ChunkStatusTasks.postLoadProtoChunk(instance, entities.stream() - .map(flowTag -> (net.minecraft.nbt.CompoundTag) Converter.convertTag(flowTag)).toList(), nmsChunk.getPos()); - } - }; -@@ -147,13 +147,13 @@ public class SlimeChunkConverter { - } - - // Height Maps -- EnumSet heightMapTypes = nmsChunk.getStatus().heightmapsAfter(); -+ EnumSet heightMapTypes = nmsChunk.getPersistedStatus().heightmapsAfter(); - CompoundMap heightMaps = chunk.getHeightMaps().getValue(); - EnumSet unsetHeightMaps = EnumSet.noneOf(Heightmap.Types.class); - - // Light -- nmsChunk.setBlockNibbles(blockNibbles); -- nmsChunk.setSkyNibbles(skyNibbles); -+ nmsChunk.starlight$setBlockNibbles(blockNibbles); -+ nmsChunk.starlight$setSkyNibbles(skyNibbles); - - for (Heightmap.Types type : heightMapTypes) { - String name = type.getSerializedName(); -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index 2265f83768c4dc29f67d29730c4be45a194727da..187336ecaa4262e3f081a88702031b17c6037091 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -1,17 +1,15 @@ - package com.infernalsuite.aswm.level; - - import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+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.aswm.Converter; --import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; --import com.infernalsuite.aswm.api.world.SlimeChunk; - import com.infernalsuite.aswm.api.world.SlimeWorld; - import com.infernalsuite.aswm.api.world.SlimeWorldInstance; - import com.infernalsuite.aswm.api.world.properties.SlimeProperties; - import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; --import io.papermc.paper.chunk.system.scheduling.ChunkLoadTask; --import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; --import io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask; -+import com.infernalsuite.aswm.serialization.slime.SlimeSerializer; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Holder; - import net.minecraft.core.registries.Registries; -@@ -24,8 +22,6 @@ import net.minecraft.util.ProgressListener; - import net.minecraft.util.Unit; - import net.minecraft.util.datafix.DataFixers; - import net.minecraft.world.Difficulty; --import net.minecraft.world.entity.EntityType; --import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.biome.Biome; - import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkGenerator; -@@ -42,7 +38,6 @@ import org.spigotmc.AsyncCatcher; - import java.io.IOException; - import java.nio.file.Files; - import java.nio.file.Path; --import java.util.ArrayList; - import java.util.Collections; - import java.util.UUID; - import java.util.concurrent.CompletableFuture; -@@ -51,7 +46,6 @@ import java.util.concurrent.Executors; - import java.util.concurrent.Future; - import java.util.function.Consumer; - import java.util.logging.Level; --import java.util.stream.Collectors; - - public class SlimeLevelInstance extends ServerLevel { - -@@ -106,7 +100,7 @@ public class SlimeLevelInstance extends ServerLevel { - @Override - public ChunkGenerator getGenerator(SlimeBootstrap slimeBootstrap) { - String biomeStr = slimeBootstrap.initial().getPropertyMap().getValue(SlimeProperties.DEFAULT_BIOME); -- ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, new ResourceLocation(biomeStr)); -+ ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, ResourceLocation.parse(biomeStr)); - Holder defaultBiome = MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolder(biomeKey).orElseThrow(); - return new SlimeLevelGenerator(defaultBiome); - } -@@ -144,12 +138,13 @@ public class SlimeLevelInstance extends ServerLevel { - return CompletableFuture.completedFuture(null); - } - -+ /* - @Override - public void saveIncrementally(boolean doFull) { - if (doFull) { - this.save(null, false, false); - } -- } -+ }*/ // Most likely unused - kyngs - - private Future saveInternal() { - synchronized (saveLock) { // Don't want to save the SlimeWorld from multiple threads simultaneously -@@ -180,6 +175,7 @@ public class SlimeLevelInstance extends ServerLevel { - return new ChunkDataLoadTask(task, scheduler, world, chunkX, chunkZ, priority, onRun); - } - -+ /* - public void loadEntities(int chunkX, int chunkZ) { - SlimeChunk slimeChunk = this.slimeInstance.getChunk(chunkX, chunkZ); - if (slimeChunk != null) { -@@ -191,7 +187,7 @@ public class SlimeLevelInstance extends ServerLevel { - .toList() - ), new ChunkPos(chunkX, chunkZ)); - } -- } -+ }*/ // Most likely unused - kyngs - - @Override - public void setDefaultSpawnPos(BlockPos pos, float angle) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 4640baec5bed6c2d53cc0f8ca1d273cc115abe9b..589cb65f79bb05ee8c44b526c707e81dc02a4761 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -1,5 +1,7 @@ - package net.minecraft.world.level.chunk; - -+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; -+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.Maps; - import com.google.common.collect.UnmodifiableIterator; -@@ -23,6 +25,7 @@ import net.minecraft.core.registries.Registries; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.FriendlyByteBuf; - import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; -+import net.minecraft.server.level.ChunkHolder; - import net.minecraft.server.level.FullChunkStatus; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.util.profiling.Profiler; -@@ -322,6 +325,12 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - } - } - -+ // ASWM start - maintain binary compatibility with 1.20.6 -+ public NewChunkHolder getChunkHolder() { -+ return this.level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos.x, chunkPos.z); -+ } -+ // ASWM end -+ - // Paper start - If loaded util - @Override - public final FluidState getFluidIfLoaded(BlockPos blockposition) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index faf7f4f3bd1fbc91a40e5549a7a5520ea3eaec37..8832efbf9f68fb0466fdc9d6eb0d0acf5d5930dd 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -469,7 +469,7 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun - SerializableChunkData.LOGGER.error("Recoverable errors when loading section [{}, {}, {}]: {}", new Object[]{chunkPos.x, y, chunkPos.z, message}); - } - -- private static Codec>> makeBiomeCodec(Registry biomeRegistry) { -+ public static Codec>> makeBiomeCodec(Registry biomeRegistry) { - return PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS)); - } - diff --git a/patches/server/0016-Fix-chunk-saving-when-unloading.patch b/patches/server/0016-Fix-chunk-saving-when-unloading.patch deleted file mode 100644 index af7d7c6d6..000000000 --- a/patches/server/0016-Fix-chunk-saving-when-unloading.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: TechStreet <80351782+TechStreetDev@users.noreply.github.com> -Date: Mon, 26 Aug 2024 21:27:08 +0100 -Subject: [PATCH] Fix chunk saving when unloading - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index 187336ecaa4262e3f081a88702031b17c6037091..6a5a8fbd8ee013828685495267283d9518d32d20 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -25,6 +25,7 @@ import net.minecraft.world.Difficulty; - import net.minecraft.world.level.biome.Biome; - 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.dimension.LevelStem; - import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.level.storage.PrimaryLevelData; -@@ -200,9 +201,9 @@ public class SlimeLevelInstance extends ServerLevel { - propertyMap.setValue(SlimeProperties.SPAWN_YAW, angle); - } - -- // @Override -- // public void unload(LevelChunk chunk) { -- // this.slimeInstance.unload(chunk); -- // super.unload(chunk); -- // } -+ @Override -+ public void unload(LevelChunk chunk) { -+ this.slimeInstance.unload(chunk); -+ super.unload(chunk); -+ } - } -\ No newline at end of file diff --git a/patches/server/0017-fix-disable-dragon-fights.patch b/patches/server/0017-fix-disable-dragon-fights.patch deleted file mode 100644 index 55111785f..000000000 --- a/patches/server/0017-fix-disable-dragon-fights.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: evlad -Date: Mon, 9 Sep 2024 21:33:11 +0300 -Subject: [PATCH] fix disable dragon fights - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index d414a0056ab27558440086ab373867bd7e28cdd2..36d0ce70ab1eaddf3354a9e2f8c86269cb319342 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -2,6 +2,7 @@ package net.minecraft.server.level; - - import com.google.common.annotations.VisibleForTesting; - import com.google.common.collect.Lists; -+import com.infernalsuite.aswm.api.world.properties.SlimeProperties; // ASP - import com.mojang.datafixers.DataFixer; - import com.mojang.datafixers.util.Pair; - import com.mojang.logging.LogUtils; -@@ -663,7 +664,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.structureCheck = new StructureCheck(this.chunkSource.chunkScanner(), this.registryAccess(), minecraftserver.getStructureManager(), this.getTypeKey(), chunkgenerator, this.chunkSource.randomState(), this, chunkgenerator.getBiomeSource(), l, datafixer); // Paper - Fix missing CB diff - 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 -- this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit -+ // ASP START -+ if (bootstrap == null || bootstrap.initial().getPropertyMap().getValue(SlimeProperties.DRAGON_BATTLE)) { -+ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit -+ } else { -+ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), new EndDragonFight.Data(false, true, true, false,Optional.empty(),Optional.empty(),Optional.empty())); // ASP - disable dragon -+ } -+ // ASP END - } else { - this.dragonFight = null; - } diff --git a/patches/server/0018-fix-chunk-pdc-getting-wiped-on-chunk-unload.patch b/patches/server/0018-fix-chunk-pdc-getting-wiped-on-chunk-unload.patch deleted file mode 100644 index 93f9d7cf2..000000000 --- a/patches/server/0018-fix-chunk-pdc-getting-wiped-on-chunk-unload.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: evlad -Date: Thu, 19 Sep 2024 20:47:32 +0300 -Subject: [PATCH] fix chunk pdc getting wiped on chunk unload - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index 9b4b18758d52d66e4abf9e40e49a32428de68b9a..f0d773977609597f2da7bf691d8f4cb983743981 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -68,7 +68,9 @@ public class NMSSlimeChunk implements SlimeChunk { - - public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference) { - this.chunk = chunk; -- this.extra = reference == null ? new CompoundTag("", new CompoundMap()) : reference.getExtraData(); -+ this.extra = new CompoundTag("", new CompoundMap()); -+ extra.getValue().put(Converter.convertTag("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound())); -+ - this.upgradeData = reference == null ? null : reference.getUpgradeData(); - } - diff --git a/patches/server/0019-1.21.3-fixes.patch b/patches/server/0019-1.21.3-fixes.patch deleted file mode 100644 index 39daf6648..000000000 --- a/patches/server/0019-1.21.3-fixes.patch +++ /dev/null @@ -1,278 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AverageGithub -Date: Sun, 10 Nov 2024 13:06:42 +0100 -Subject: [PATCH] 1.21.3 fixes - - -diff --git a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -index 87b6ee19fd165bde2db3a57545c58251dc6bad22..7b65a8d9137cd160b1c69dfafe70c693e9cfd508 100644 ---- a/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -+++ b/src/main/java/com/infernalsuite/aswm/SlimeNMSBridgeImpl.java -@@ -213,7 +213,7 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - }; - - ResourceKey worldKey = dimensionOverride == null ? ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(worldName.toLowerCase(Locale.ENGLISH))) : dimensionOverride; -- LevelStem stem = MinecraftServer.getServer().registries().compositeAccess().registryOrThrow(Registries.LEVEL_STEM).get(dimension); -+ LevelStem stem = MinecraftServer.getServer().registries().compositeAccess().lookupOrThrow(Registries.LEVEL_STEM).get(dimension).orElseThrow().value(); - - SlimeLevelInstance level; - -@@ -224,7 +224,7 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - } - - // level.setReady(true); -- level.setSpawnSettings(world.getPropertyMap().getValue(SlimeProperties.ALLOW_MONSTERS), world.getPropertyMap().getValue(SlimeProperties.ALLOW_ANIMALS)); -+ level.setSpawnSettings(world.getPropertyMap().getValue(SlimeProperties.ALLOW_MONSTERS)); - - var nmsExtraData = (CompoundTag) Converter.convertTag(world.getExtraData()); - -@@ -244,7 +244,7 @@ public class SlimeNMSBridgeImpl implements SlimeNMSBridge { - String worldName = world.getName(); - - LevelSettings worldsettings = new LevelSettings(worldName, serverProps.gamemode, false, serverProps.difficulty, -- true, new GameRules(), mcServer.worldLoader.dataConfiguration()); -+ true, new GameRules(net.minecraft.world.flag.FeatureFlagSet.of()), mcServer.worldLoader.dataConfiguration()); - - WorldOptions worldoptions = new WorldOptions(0, false, false); - -diff --git a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -index b5a1f75314aac73fb77e139398017b16acbb8efb..547e8e8824795a601b2028a6007057808eddbb65 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/ChunkDataLoadTask.java -@@ -1,6 +1,7 @@ - package com.infernalsuite.aswm.level; - --import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.util.Priority; - 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; -@@ -36,7 +37,7 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - private final ChunkLoadTask chunkLoadTask; - - protected ChunkDataLoadTask(ChunkLoadTask chunkLoadTask, final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, -- final int chunkZ, final PrioritisedExecutor.Priority priority, final Consumer> onRun) { -+ final int chunkZ, final Priority priority, final Consumer> onRun) { - this.chunkLoadTask = chunkLoadTask; - this.scheduler = scheduler; - this.world = world; -@@ -86,22 +87,22 @@ public final class ChunkDataLoadTask implements CommonLoadTask { - } - - @Override -- public PrioritisedExecutor.Priority getPriority() { -+ public Priority getPriority() { - return this.task.getPriority(); - } - - @Override -- public void setPriority(PrioritisedExecutor.Priority priority) { -+ public void setPriority(Priority priority) { - this.task.setPriority(priority); - } - - @Override -- public void raisePriority(PrioritisedExecutor.Priority priority) { -+ public void raisePriority(Priority priority) { - this.task.raisePriority(priority); - } - - @Override -- public void lowerPriority(PrioritisedExecutor.Priority priority) { -+ public void lowerPriority(Priority priority) { - this.task.lowerPriority(priority); - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java b/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java -index fc6e46972bcc77134ed718c8c157ec3893d4bcdf..ceaa09deabf53f061185639660a7706c3ff27801 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java -+++ b/src/main/java/com/infernalsuite/aswm/level/CommonLoadTask.java -@@ -1,18 +1,19 @@ - package com.infernalsuite.aswm.level; - --import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.util.Priority; - - public interface CommonLoadTask { - - boolean schedule(boolean schedule); - -- PrioritisedExecutor.Priority getPriority(); -+ Priority getPriority(); - - boolean cancel(); - -- void lowerPriority(PrioritisedExecutor.Priority priority); -+ void lowerPriority(Priority priority); - -- void raisePriority(PrioritisedExecutor.Priority priority); -+ void raisePriority(Priority priority); - -- void setPriority(PrioritisedExecutor.Priority priority); -+ void setPriority(Priority priority); - } -\ No newline at end of file -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index f0d773977609597f2da7bf691d8f4cb983743981..cbcbbc8410b24daf685d74791ad80989d4830d7b 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -29,7 +29,7 @@ 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.storage.ChunkSerializer; -+import net.minecraft.world.level.chunk.storage.SerializableChunkData; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.lighting.LevelLightEngine; - import org.slf4j.Logger; -@@ -49,14 +49,14 @@ public class NMSSlimeChunk implements SlimeChunk { - static { - { - PalettedContainer empty = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); -- Tag tag = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); -+ Tag tag = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); - - EMPTY_BLOCK_STATE_PALETTE = (CompoundTag) Converter.convertTag("", tag); - } - { -- Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME); -- PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); -- Tag tag = ChunkSerializer.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); -+ Registry biomes = net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME); -+ PalettedContainer> empty = new PalettedContainer<>(biomes.asHolderIdMap(), biomes.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); -+ Tag tag = SerializableChunkData.makeBiomeCodec(biomes).encodeStart(NbtOps.INSTANCE, empty).getOrThrow(); - - EMPTY_BIOME_PALETTE = (CompoundTag) Converter.convertTag("", tag); - } -@@ -89,10 +89,10 @@ public class NMSSlimeChunk implements SlimeChunk { - SlimeChunkSection[] sections = new SlimeChunkSection[this.chunk.getSectionsCount()]; - LevelLightEngine lightEngine = chunk.getLevel().getChunkSource().getLightEngine(); - -- Registry biomeRegistry = chunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME); -+ Registry biomeRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.BIOME); - - // Ignore deprecation, spigot only method -- Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS)); -+ Codec>> codec = PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow()); - - for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { - LevelChunkSection section = chunk.getSections()[sectionId]; -@@ -111,7 +111,7 @@ public class NMSSlimeChunk implements SlimeChunk { - if (section.hasOnlyAir()) { - blockStateTag = EMPTY_BLOCK_STATE_PALETTE; - } else { -- Tag data = ChunkSerializer.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling -+ Tag data = SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, section.getStates()).getOrThrow(); // todo error handling - blockStateTag = (CompoundTag) Converter.convertTag("", data); - } - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -index 86a5b457bdca63713769d2b708be905d72ff76a3..7623584e60dddd5558f22eed4403944fafee0696 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkConverter.java -@@ -28,7 +28,7 @@ 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.ChunkSerializer; -+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; -@@ -54,10 +54,10 @@ public class SlimeChunkConverter { - instance.getLightEngine().retainData(pos, true); - }); - -- Registry biomeRegistry = instance.registryAccess().registryOrThrow(Registries.BIOME); -+ Registry biomeRegistry = instance.registryAccess().lookupOrThrow(Registries.BIOME); - // Ignore deprecated method - -- Codec>> codec = PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS), null); -+ Codec>> codec = PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.get(Biomes.PLAINS).orElseThrow(), null); - - for (int sectionId = 0; sectionId < chunk.getSections().length; sectionId++) { - SlimeChunkSection slimeSection = chunk.getSections()[sectionId]; -@@ -75,7 +75,7 @@ public class SlimeChunkConverter { - - PalettedContainer blockPalette; - if (slimeSection.getBlockStatesTag() != null) { -- DataResult> dataresult = ChunkSerializer.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { -+ DataResult> dataresult = SerializableChunkData.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, Converter.convertTag(slimeSection.getBlockStatesTag())).promotePartial((s) -> { - System.out.println("Recoverable error when parsing section " + x + "," + z + ": " + s); // todo proper logging - }); - blockPalette = dataresult.getOrThrow(); // todo proper logging -@@ -91,7 +91,7 @@ public class SlimeChunkConverter { - }); - biomePalette = dataresult.getOrThrow(); // todo proper logging - } else { -- biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); -+ biomePalette = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.get(Biomes.PLAINS).orElseThrow(), PalettedContainer.Strategy.SECTION_BIOMES, null); - } - - if (sectionId < sections.length) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index 6a5a8fbd8ee013828685495267283d9518d32d20..acb6ea84fbc40a6907edb237b834feeb66075af8 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -1,6 +1,7 @@ - package com.infernalsuite.aswm.level; - --import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.util.Priority; - 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; -@@ -80,8 +81,8 @@ public class SlimeLevelInstance extends ServerLevel { - super(slimeBootstrap, MinecraftServer.getServer(), MinecraftServer.getServer().executor, - CUSTOM_LEVEL_STORAGE.createAccess(slimeBootstrap.initial().getName() + UUID.randomUUID(), dimensionKey), - primaryLevelData, worldKey, worldDimension, -- MinecraftServer.getServer().progressListenerFactory.create(11), false, null, 0, -- Collections.emptyList(), true, environment, null, null); -+ MinecraftServer.getServer().progressListenerFactory.create(11), false, 0, -+ Collections.emptyList(), true, null, environment, null, null); - this.slimeInstance = new SlimeInMemoryWorld(slimeBootstrap, this); - - -@@ -93,7 +94,7 @@ public class SlimeLevelInstance extends ServerLevel { - propertyMap.getValue(SlimeProperties.SPAWN_Y), - propertyMap.getValue(SlimeProperties.SPAWN_Z)), - propertyMap.getValue(SlimeProperties.SPAWN_YAW)); -- super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS), propertyMap.getValue(SlimeProperties.ALLOW_ANIMALS)); -+ super.setSpawnSettings(propertyMap.getValue(SlimeProperties.ALLOW_MONSTERS)); - - this.pvpMode = propertyMap.getValue(SlimeProperties.PVP); - } -@@ -102,7 +103,7 @@ public class SlimeLevelInstance extends ServerLevel { - public ChunkGenerator getGenerator(SlimeBootstrap slimeBootstrap) { - String biomeStr = slimeBootstrap.initial().getPropertyMap().getValue(SlimeProperties.DEFAULT_BIOME); - ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, ResourceLocation.parse(biomeStr)); -- Holder defaultBiome = MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolder(biomeKey).orElseThrow(); -+ Holder defaultBiome = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME).get(biomeKey).orElseThrow(); - return new SlimeLevelGenerator(defaultBiome); - } - -@@ -172,7 +173,7 @@ public class SlimeLevelInstance extends ServerLevel { - return this.slimeInstance; - } - -- public ChunkDataLoadTask getLoadTask(ChunkLoadTask task, ChunkTaskScheduler scheduler, ServerLevel world, int chunkX, int chunkZ, PrioritisedExecutor.Priority priority, Consumer> onRun) { -+ public ChunkDataLoadTask getLoadTask(ChunkLoadTask task, ChunkTaskScheduler scheduler, ServerLevel world, int chunkX, int chunkZ, Priority priority, Consumer> onRun) { - return new ChunkDataLoadTask(task, scheduler, world, chunkX, chunkZ, priority, onRun); - } - -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 8832efbf9f68fb0466fdc9d6eb0d0acf5d5930dd..d03db9b8e78c795408b336beb53d6940a75ed759 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -371,7 +371,7 @@ public record SerializableChunkData(Registry biomeRegistry, ChunkPos chun - } - - if (world instanceof com.infernalsuite.aswm.level.SlimeLevelInstance) { -- poiStorage.checkConsistencyWithBlocks(SectionPos.of(chunkPos.getWorldPosition()), achunksection[j]); -+ poiStorage.checkConsistencyWithBlocks(sectionposition, serializablechunkdata_b.chunkSection); - } - } - } diff --git a/patches/server/0020-Fix-missing-chunks-entities-when-chunk-saving.patch b/patches/server/0020-Fix-missing-chunks-entities-when-chunk-saving.patch deleted file mode 100644 index cacb07135..000000000 --- a/patches/server/0020-Fix-missing-chunks-entities-when-chunk-saving.patch +++ /dev/null @@ -1,132 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: David Mayr -Date: Thu, 5 Dec 2024 01:41:21 +0100 -Subject: [PATCH] Fix missing chunks & entities when chunk saving - - -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -index bc0990458df745c92e5bc0530ff35ab992365b3a..682f293396ef34fdc6a61314827dc34f504c7777 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java -@@ -897,6 +897,13 @@ public final class NewChunkHolder { - - final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk); - -+ // ASP start - Chunk unloading -+ if (world instanceof com.infernalsuite.aswm.level.SlimeLevelInstance slime && chunk instanceof LevelChunk levelChunk) { -+ //The custom entity slices need to be passed on for entities to be saved -+ slime.onChunkUnloaded(levelChunk, entityChunk); -+ } -+ // ASP end - Chunk unloading -+ - // unload chunk data - if (chunk != null) { - if (chunk instanceof LevelChunk levelChunk) { -diff --git a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -index cbcbbc8410b24daf685d74791ad80989d4830d7b..cdc4fd3a8767a3cb168ceb3088f4ae237fd9a11e 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -+++ b/src/main/java/com/infernalsuite/aswm/level/NMSSlimeChunk.java -@@ -66,12 +66,19 @@ public class NMSSlimeChunk implements SlimeChunk { - private final CompoundTag extra; - private final CompoundTag upgradeData; - -+ private final ChunkEntitySlices entitySlices; -+ - public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference) { -+ this(chunk, reference, null); -+ } -+ -+ public NMSSlimeChunk(LevelChunk chunk, SlimeChunk reference, ChunkEntitySlices slices) { - this.chunk = chunk; - this.extra = new CompoundTag("", new CompoundMap()); - extra.getValue().put(Converter.convertTag("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound())); - - this.upgradeData = reference == null ? null : reference.getUpgradeData(); -+ this.entitySlices = slices; - } - - @Override -@@ -168,11 +175,7 @@ public class NMSSlimeChunk implements SlimeChunk { - public List getEntities() { - List entities = new ArrayList<>(); - -- if(this.chunk == null || this.chunk.getChunkHolder() == null) { -- return new ArrayList<>(); -- } -- -- ChunkEntitySlices slices = this.chunk.getChunkHolder().getEntityChunk(); -+ ChunkEntitySlices slices = getEntitySlices(); - if (slices == null) { - return new ArrayList<>(); - } -@@ -194,6 +197,18 @@ public class NMSSlimeChunk implements SlimeChunk { - }); - } - -+ private ChunkEntitySlices getEntitySlices() { -+ if (this.entitySlices != null) { -+ return entitySlices; -+ } -+ -+ if (this.chunk == null || this.chunk.getChunkHolder() == null) { -+ return null; -+ } -+ -+ return this.chunk.getChunkHolder().getEntityChunk(); -+ } -+ - @Override - public CompoundTag getExtraData() { - return extra; -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -index 2ebabf20c37d2b5c479de5bb241aa334f92a1104..866246838b6d6f23eacb1d9bad1c31cb2c1e76b0 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeChunkLevel.java -@@ -19,12 +19,6 @@ public class SlimeChunkLevel extends LevelChunk { - this.inMemoryWorld = world.slimeInstance; - } - -- @Override -- public void unloadCallback() { -- super.unloadCallback(); -- this.inMemoryWorld.unload(this); -- } -- - @Override - public void loadCallback() { - super.loadCallback(); -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 770679851baba2ddb9f8f427f4cd80ea8b32122b..619ccbab1a5582af1d1ad69fb0c54e52ca84847d 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -104,11 +104,11 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - - // Authored by: Kenox - // Don't use the NMS live chunk in the chunk map -- public void unload(LevelChunk providedChunk) { -+ public void unload(LevelChunk providedChunk, ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices entitySlices) { - final int x = providedChunk.locX; - final int z = providedChunk.locZ; - -- SlimeChunk chunk = new NMSSlimeChunk(providedChunk, getChunk(x, z)); -+ SlimeChunk chunk = new NMSSlimeChunk(providedChunk, getChunk(x, z), entitySlices); - - if (FastChunkPruner.canBePruned(this.liveWorld, providedChunk)) { - this.chunkStorage.remove(Util.chunkPosition(x, z)); -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -index acb6ea84fbc40a6907edb237b834feeb66075af8..2821d953e8b01cadf171508ac0b8e1eda6201970 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeLevelInstance.java -@@ -202,9 +202,7 @@ public class SlimeLevelInstance extends ServerLevel { - propertyMap.setValue(SlimeProperties.SPAWN_YAW, angle); - } - -- @Override -- public void unload(LevelChunk chunk) { -- this.slimeInstance.unload(chunk); -- super.unload(chunk); -+ public void onChunkUnloaded(LevelChunk chunk, ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices entityChunk) { -+ this.slimeInstance.unload(chunk, entityChunk); - } - } -\ No newline at end of file diff --git a/patches/server/0021-fix-pdc-not-saving-when-chunks-are-unloaded.patch b/patches/server/0021-fix-pdc-not-saving-when-chunks-are-unloaded.patch deleted file mode 100644 index e220d4ca3..000000000 --- a/patches/server/0021-fix-pdc-not-saving-when-chunks-are-unloaded.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: David Mayr -Date: Thu, 5 Dec 2024 01:46:03 +0100 -Subject: [PATCH] fix pdc not saving when chunks are unloaded - - -diff --git a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -index 619ccbab1a5582af1d1ad69fb0c54e52ca84847d..8c71a932d49d55d861910712d3f482d39b62ad65 100644 ---- a/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -+++ b/src/main/java/com/infernalsuite/aswm/level/SlimeInMemoryWorld.java -@@ -114,6 +114,8 @@ public class SlimeInMemoryWorld implements SlimeWorld, SlimeWorldInstance { - this.chunkStorage.remove(Util.chunkPosition(x, z)); - return; - } -+ Tag pdcTag = Converter.convertTag("ChunkBukkitValues", providedChunk.persistentDataContainer.toTagCompound()); -+ chunk.getExtraData().getValue().put(pdcTag); - - this.chunkStorage.put(Util.chunkPosition(x, z), - new SlimeChunkSkeleton(chunk.getX(), chunk.getZ(), chunk.getSections(), diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 818ff7f56..d71cf8060 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,43 +1,37 @@ plugins { - id("com.gradleup.shadow") version "8.3.5" - id("net.kyori.blossom") version "2.1.0" -} - -version = "3.0.0" - -sourceSets { - main { - blossom { - resources { - property("version", version.toString()) - } - } - } + id("asp.base-conventions") + id("asp.publishing-conventions") + id("net.minecrell.plugin-yml.paper") + id("com.gradleup.shadow") } dependencies { compileOnly(project(":api")) - implementation(project(":loaders")) - implementation("org.spongepowered:configurate-yaml:4.1.2") - implementation("org.bstats:bstats-bukkit:3.0.0") - implementation("org.incendo:cloud-paper:2.0.0-beta.9") - implementation("org.incendo:cloud-minecraft-extras:2.0.0-beta.9") - implementation("org.incendo:cloud-annotations:2.0.0-rc.1") - compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") + + implementation(libs.configurate.yaml) + implementation(libs.bstats) + implementation(libs.cloud.paper) + implementation(libs.cloud.minecraft.extras) + implementation(libs.cloud.annotations) + + compileOnly(paperApi()) } tasks { + withType { + archiveBaseName.set("asp-plugin") + } + shadowJar { archiveClassifier.set("") - - relocate("org.bstats", "com.grinderwolf.swm.internal.bstats") - relocate("ninja.leaping.configurate", "com.grinderwolf.swm.internal.configurate") - //relocate("com.flowpowered.nbt", "com.grinderwolf.swm.internal.nbt") - relocate("com.zaxxer.hikari", "com.grinderwolf.swm.internal.hikari") - relocate("com.mongodb", "com.grinderwolf.swm.internal.mongodb") - relocate("io.lettuce", "com.grinderwolf.swm.internal.lettuce") - relocate("org.bson", "com.grinderwolf.swm.internal.bson") + + relocate("org.bstats", "com.infernalsuite.asp.libs.bstats") + relocate("org.spongepowered.configurate", "com.infernalsuite.asp.libs.configurate") + relocate("com.zaxxer.hikari", "com.infernalsuite.asp.libs.hikari") + relocate("com.mongodb", "com.infernalsuite.asp.libs.mongo") + relocate("io.lettuce", "com.infernalsuite.asp.libs.lettuce") + relocate("org.bson", "com.infernalsuite.asp.libs.bson") } assemble { @@ -45,4 +39,11 @@ tasks { } } -description = "slimeworldplugin" +paper { + name = "ASPaperPlugin" + description = "ASP plugin for Paper, providing utilities for the ASP platform" + version = "\${gitCommitId}" + apiVersion = "1.21" + main = "com.infernalsuite.asp.plugin.SWPlugin" + authors = listOf("InfernalSuite") +} diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/SWPlugin.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java similarity index 76% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/SWPlugin.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java index a32f83a24..7f85212f9 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/SWPlugin.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/SWPlugin.java @@ -1,18 +1,18 @@ -package com.infernalsuite.aswm.plugin; - -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; -import com.infernalsuite.aswm.plugin.loader.LoaderManager; -import com.infernalsuite.aswm.api.AdvancedSlimePaperAPI; -import com.infernalsuite.aswm.api.SlimeNMSBridge; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +package com.infernalsuite.asp.plugin; + +import com.infernalsuite.asp.plugin.commands.CommandManager; +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.loader.LoaderManager; +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -20,6 +20,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.*; +import java.util.logging.Level; public class SWPlugin extends JavaPlugin { @@ -40,7 +41,7 @@ public LoaderManager getLoaderManager() { @Override public void onLoad() { try { - ConfigManager.initialize(); + com.infernalsuite.asp.plugin.config.ConfigManager.initialize(); } catch (NullPointerException | IOException ex) { getSLF4JLogger().error("Failed to load config files", ex); return; @@ -97,6 +98,27 @@ public void onEnable() { worldsToLoad.clear(); // Don't unnecessarily hog up memory } + @Override + public void onDisable() { + WorldsConfig config = ConfigManager.getWorldConfig(); + + for (Map.Entry entry : config.getWorlds().entrySet()) { + SlimeWorld world = ASP.getLoadedWorld(entry.getKey()); + if(world == null) { + continue; + } + + if (!world.isReadOnly()) { + try { + ASP.saveWorld(world); //Save the world sync + } catch (RuntimeException | IOException ex) { + getLogger().log(Level.SEVERE, "Failed to save world " + world.getName(), ex); + } + } + Bukkit.unloadWorld(world.getName(), false); //Unload without saving as we have just saved (if not read only) + } + } + private List loadWorlds() { List erroredWorlds = new ArrayList<>(); WorldsConfig config = ConfigManager.getWorldConfig(); diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/CommandManager.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/CommandManager.java similarity index 66% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/CommandManager.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/CommandManager.java index ee801836e..8ccc06739 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/CommandManager.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/CommandManager.java @@ -1,11 +1,6 @@ -package com.infernalsuite.aswm.plugin.commands; - -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.SWPlugin; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.*; -import com.infernalsuite.aswm.plugin.commands.parser.suggestion.KnownSlimeWorldSuggestionProvider; -import com.infernalsuite.aswm.plugin.commands.sub.*; +package com.infernalsuite.asp.plugin.commands; + +import com.infernalsuite.asp.api.world.SlimeWorld; import io.leangen.geantyref.TypeToken; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -14,16 +9,13 @@ import org.incendo.cloud.annotations.AnnotationParser; import org.incendo.cloud.annotations.Command; import org.incendo.cloud.bukkit.CloudBukkitCapabilities; -import org.incendo.cloud.component.DefaultValue; import org.incendo.cloud.exception.ArgumentParseException; import org.incendo.cloud.exception.CommandExecutionException; import org.incendo.cloud.exception.InvalidSyntaxException; import org.incendo.cloud.exception.NoPermissionException; import org.incendo.cloud.exception.handling.ExceptionHandler; import org.incendo.cloud.execution.ExecutionCoordinator; -import org.incendo.cloud.minecraft.extras.MinecraftHelp; import org.incendo.cloud.paper.LegacyPaperCommandManager; -import org.incendo.cloud.paper.PaperCommandManager; import org.incendo.cloud.parser.ParserRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,17 +23,15 @@ import java.util.HashSet; import java.util.Set; -import static org.incendo.cloud.parser.standard.StringParser.greedyStringParser; - public class CommandManager { private static final Logger LOGGER = LoggerFactory.getLogger(CommandManager.class); // A list containing all the worlds that are being performed operations on, so two commands cannot be run at the same time private final Set worldsInUse = new HashSet<>(); - private final SWPlugin plugin; + private final com.infernalsuite.asp.plugin.SWPlugin plugin; - public CommandManager(SWPlugin plugin) { + public CommandManager(com.infernalsuite.asp.plugin.SWPlugin plugin) { LegacyPaperCommandManager commandManager = LegacyPaperCommandManager.createNative( plugin, @@ -57,17 +47,17 @@ public CommandManager(SWPlugin plugin) { ParserRegistry parserRegistry = commandManager.parserRegistry(); - parserRegistry.registerSuggestionProvider("known-slime-worlds", new KnownSlimeWorldSuggestionProvider()); + parserRegistry.registerSuggestionProvider("known-slime-worlds", new com.infernalsuite.asp.plugin.commands.parser.suggestion.KnownSlimeWorldSuggestionProvider()); - parserRegistry.registerParserSupplier(TypeToken.get(NamedWorldData.class), parserParameters -> new NamedWorldDataParser()); - parserRegistry.registerParserSupplier(TypeToken.get(SlimeWorld.class), parserParameters -> new SlimeWorldParser()); - parserRegistry.registerParserSupplier(TypeToken.get(NamedSlimeLoader.class), parserParameters -> new NamedSlimeLoaderParser(plugin.getLoaderManager())); - parserRegistry.registerParserSupplier(TypeToken.get(World.class), parserParameters -> new BukkitWorldParser()); + parserRegistry.registerParserSupplier(TypeToken.get(com.infernalsuite.asp.plugin.commands.parser.NamedWorldData.class), parserParameters -> new com.infernalsuite.asp.plugin.commands.parser.NamedWorldDataParser()); + parserRegistry.registerParserSupplier(TypeToken.get(SlimeWorld.class), parserParameters -> new com.infernalsuite.asp.plugin.commands.parser.SlimeWorldParser()); + parserRegistry.registerParserSupplier(TypeToken.get(com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader.class), parserParameters -> new com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoaderParser(plugin.getLoaderManager())); + parserRegistry.registerParserSupplier(TypeToken.get(World.class), parserParameters -> new com.infernalsuite.asp.plugin.commands.parser.BukkitWorldParser()); commandManager.exceptionController().registerHandler(TypeToken.get(CommandExecutionException.class), ExceptionHandler.unwrappingHandler()); // Unwrap the exception commandManager.exceptionController().registerHandler(TypeToken.get(ArgumentParseException.class), context -> { Throwable cause = context.exception().getCause(); - if (cause instanceof MessageCommandException message) { + if (cause instanceof com.infernalsuite.asp.plugin.commands.exception.MessageCommandException message) { context.context().sender().sendMessage(message.getComponent()); } else { String message = cause.getMessage(); @@ -101,29 +91,29 @@ public CommandManager(SWPlugin plugin) { )); }); - commandManager.exceptionController().registerHandler(TypeToken.get(MessageCommandException.class), context -> { + commandManager.exceptionController().registerHandler(TypeToken.get(com.infernalsuite.asp.plugin.commands.exception.MessageCommandException.class), context -> { context.context().sender().sendMessage(context.exception().getComponent()); }); AnnotationParser ap = new AnnotationParser<>(commandManager, CommandSender.class); ap.parse(this, - new CloneWorldCmd(this), - new CreateWorldCmd(this), - new DeleteWorldCmd(this), - new DSListCmd(this), - new GotoCmd(this), - new ImportWorldCmd(this), - new LoadTemplateWorldCmd(this), - new LoadWorldCmd(this), - new MigrateWorldCmd(this), - new ReloadConfigCmd(this), - new SaveWorldCmd(this), - new SetSpawnCmd(this), - new UnloadWorldCmd(this), - new VersionCmd(this), - new WorldListCmd(this), - new HelpCmd(this, commandManager) + new com.infernalsuite.asp.plugin.commands.sub.CloneWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.CreateWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.DeleteWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.DSListCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.GotoCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.ImportWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.LoadTemplateWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.LoadWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.MigrateWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.ReloadConfigCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.SaveWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.SetSpawnCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.UnloadWorldCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.VersionCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.WorldListCmd(this), + new com.infernalsuite.asp.plugin.commands.sub.HelpCmd(this, commandManager) ); } @@ -141,7 +131,7 @@ public void onCommand(CommandSender sender) { )); } - SWPlugin getPlugin() { + com.infernalsuite.asp.plugin.SWPlugin getPlugin() { return plugin; } diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/SlimeCommand.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/SlimeCommand.java similarity index 63% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/SlimeCommand.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/SlimeCommand.java index 9496fcd46..34b6499ad 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/SlimeCommand.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/SlimeCommand.java @@ -1,13 +1,12 @@ -package com.infernalsuite.aswm.plugin.commands; +package com.infernalsuite.asp.plugin.commands; -import com.infernalsuite.aswm.api.AdvancedSlimePaperAPI; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.plugin.SWPlugin; +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -19,7 +18,7 @@ public class SlimeCommand { ); protected final CommandManager commandManager; - protected final SWPlugin plugin; + protected final com.infernalsuite.asp.plugin.SWPlugin plugin; protected final AdvancedSlimePaperAPI asp = AdvancedSlimePaperAPI.instance(); public SlimeCommand(CommandManager commandManager) { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/exception/MessageCommandException.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/exception/MessageCommandException.java similarity index 84% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/exception/MessageCommandException.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/exception/MessageCommandException.java index e6eab23d5..385fb9398 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/exception/MessageCommandException.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/exception/MessageCommandException.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.plugin.commands.exception; +package com.infernalsuite.asp.plugin.commands.exception; import net.kyori.adventure.text.Component; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/BukkitWorldParser.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/BukkitWorldParser.java similarity index 83% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/BukkitWorldParser.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/BukkitWorldParser.java index 3adcb4db9..be78f128f 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/BukkitWorldParser.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/BukkitWorldParser.java @@ -1,7 +1,6 @@ -package com.infernalsuite.aswm.plugin.commands.parser; +package com.infernalsuite.asp.plugin.commands.parser; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -24,7 +23,7 @@ public class BukkitWorldParser implements ArgumentParser { World loaded = Bukkit.getWorld(input); if (loaded == null) { - return ArgumentParseResult.failure(new MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( + return ArgumentParseResult.failure(new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( Component.text("World " + input + " is not loaded!").color(NamedTextColor.RED) ))); } diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoader.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoader.java similarity index 57% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoader.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoader.java index e27aef7d1..5a7ed2a7b 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoader.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoader.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.plugin.commands.parser; +package com.infernalsuite.asp.plugin.commands.parser; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.loaders.SlimeLoader; public record NamedSlimeLoader(String name, SlimeLoader slimeLoader) { @Override diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoaderParser.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoaderParser.java similarity index 81% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoaderParser.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoaderParser.java index c9df2af8f..9b5efea97 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedSlimeLoaderParser.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedSlimeLoaderParser.java @@ -1,9 +1,8 @@ -package com.infernalsuite.aswm.plugin.commands.parser; +package com.infernalsuite.asp.plugin.commands.parser; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.loader.LoaderManager; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.loader.LoaderManager; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; @@ -31,7 +30,7 @@ public NamedSlimeLoaderParser(LoaderManager loaderManager) { SlimeLoader loader = loaderManager.getLoader(input); if (loader == null) { - return ArgumentParseResult.failure(new MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( + return ArgumentParseResult.failure(new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(SlimeCommand.COMMAND_PREFIX.append( Component.text("Unknown data source " + input + "!").color(NamedTextColor.RED) ))); } diff --git a/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldData.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldData.java new file mode 100644 index 000000000..b15ab60bc --- /dev/null +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldData.java @@ -0,0 +1,6 @@ +package com.infernalsuite.asp.plugin.commands.parser; + +import com.infernalsuite.asp.plugin.config.WorldData; + +public record NamedWorldData(String name, WorldData worldData) { +} diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldDataParser.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldDataParser.java similarity index 77% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldDataParser.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldDataParser.java index 51f5743f5..036e7045b 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldDataParser.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/NamedWorldDataParser.java @@ -1,10 +1,10 @@ -package com.infernalsuite.aswm.plugin.commands.parser; +package com.infernalsuite.asp.plugin.commands.parser; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.suggestion.KnownSlimeWorldSuggestionProvider; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.plugin.commands.parser.suggestion.KnownSlimeWorldSuggestionProvider; +import com.infernalsuite.asp.plugin.config.ConfigManager; +import com.infernalsuite.asp.plugin.config.WorldData; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/SlimeWorldParser.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/SlimeWorldParser.java similarity index 85% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/SlimeWorldParser.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/SlimeWorldParser.java index efe1e178b..ed6ddea8e 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/SlimeWorldParser.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/SlimeWorldParser.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.parser; +package com.infernalsuite.asp.plugin.commands.parser; -import com.infernalsuite.aswm.api.AdvancedSlimePaperAPI; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java similarity index 88% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java index 25e36ae5b..4a57928d8 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/parser/suggestion/KnownSlimeWorldSuggestionProvider.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.plugin.commands.parser.suggestion; +package com.infernalsuite.asp.plugin.commands.parser.suggestion; -import com.infernalsuite.aswm.plugin.config.ConfigManager; +import com.infernalsuite.asp.plugin.config.ConfigManager; import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.context.CommandContext; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CloneWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CloneWorldCmd.java similarity index 69% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CloneWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CloneWorldCmd.java index 78a128396..fd4e17e7d 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CloneWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CloneWorldCmd.java @@ -1,18 +1,8 @@ -package com.infernalsuite.aswm.plugin.commands.sub; - - -import com.infernalsuite.aswm.api.exceptions.*; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; -import com.infernalsuite.aswm.plugin.commands.parser.NamedWorldData; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; -import com.infernalsuite.aswm.plugin.util.ExecutorUtil; +package com.infernalsuite.asp.plugin.commands.sub; + + +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -29,39 +19,39 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; -public class CloneWorldCmd extends SlimeCommand { +public class CloneWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(CloneWorldCmd.class); - public CloneWorldCmd(CommandManager commandManager) { + public CloneWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @Command("swp|aswm|swm clone-world [new-data-source]") @CommandDescription("Clones a world") @Permission("swm.cloneworld") - public CompletableFuture cloneWorld(CommandSender sender, @Argument(value = "template-world") NamedWorldData templateWorld, + public CompletableFuture cloneWorld(CommandSender sender, @Argument(value = "template-world") com.infernalsuite.asp.plugin.commands.parser.NamedWorldData templateWorld, @Argument(value = "world-name") String worldName, - @Argument(value = "new-data-source") @Nullable NamedSlimeLoader slimeLoader) { + @Argument(value = "new-data-source") @Nullable com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader slimeLoader) { World world = Bukkit.getWorld(worldName); if (world != null) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already loaded!")).color(NamedTextColor.RED) ); } - WorldsConfig config = ConfigManager.getWorldConfig(); - WorldData worldData = templateWorld.worldData(); + com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); + com.infernalsuite.asp.plugin.config.WorldData worldData = templateWorld.worldData(); if (templateWorld.name().equals(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("The template world name cannot be the same as the cloned world one!")).color(NamedTextColor.RED) ); } if (commandManager.getWorldsInUse().contains(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already being used on another command! Wait some time and try again.")).color(NamedTextColor.RED) ); } @@ -86,13 +76,13 @@ public CompletableFuture cloneWorld(CommandSender sender, @Argument(value SlimeWorld slimeWorld = getWorldReadyForCloning(templateWorld.name(), initLoader, templateWorld.worldData().toPropertyMap()); SlimeWorld finalSlimeWorld = slimeWorld.clone(worldName, dataSource); - ExecutorUtil.runSyncAndWait(plugin, () -> { + com.infernalsuite.asp.plugin.util.ExecutorUtil.runSyncAndWait(plugin, () -> { try { asp.loadWorld(finalSlimeWorld, true); config.getWorlds().put(worldName, worldData); } catch (IllegalArgumentException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to generate world " + worldName + ": " + ex.getMessage() + ".").color(NamedTextColor.RED) )); } @@ -103,30 +93,30 @@ public CompletableFuture cloneWorld(CommandSender sender, @Argument(value )); }); config.save(); - } catch (WorldAlreadyExistsException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + } catch (com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException ex) { + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("There is already a world called " + worldName + " stored in " + dataSource + ".").color(NamedTextColor.RED) )); - } catch (CorruptedWorldException ex) { + } catch (com.infernalsuite.asp.api.exceptions.CorruptedWorldException ex) { LOGGER.error("Failed to load world {}: world seems to be corrupted.", templateWorld.name(), ex); - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world " + templateWorld.name() + ": world seems to be corrupted.").color(NamedTextColor.RED) )); - } catch (NewerFormatException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + } catch (com.infernalsuite.asp.api.exceptions.NewerFormatException ex) { + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world " + templateWorld.name() + ": this world was serialized with a newer version of the Slime Format (" + ex.getMessage() + ") that SWM cannot understand.").color(NamedTextColor.RED) )); - } catch (UnknownWorldException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + } catch (com.infernalsuite.asp.api.exceptions.UnknownWorldException ex) { + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world " + templateWorld.name() + ": world could not be found (using data source '" + worldData.getDataSource() + "').").color(NamedTextColor.RED) )); } catch (IllegalArgumentException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world " + templateWorld.name() + ": " + ex.getMessage()).color(NamedTextColor.RED) )); } catch (IOException ex) { LOGGER.error("Failed to load world {}.", templateWorld.name(), ex); - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world " + templateWorld.name() + ". Take a look at the server console for more information.").color(NamedTextColor.RED) )); } finally { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CreateWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java similarity index 74% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CreateWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java index 6685df336..b1634d86e 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/CreateWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/CreateWorldCmd.java @@ -1,17 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.sub; - - -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; -import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; -import com.infernalsuite.aswm.plugin.util.ExecutorUtil; +package com.infernalsuite.asp.plugin.commands.sub; + + +import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -29,10 +21,10 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; -public class CreateWorldCmd extends SlimeCommand { +public class CreateWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(CreateWorldCmd.class); - public CreateWorldCmd(CommandManager commandManager) { + public CreateWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @@ -40,10 +32,10 @@ public CreateWorldCmd(CommandManager commandManager) { @CommandDescription("Create an empty world") @Permission("swm.createworld") public CompletableFuture createWorld(CommandSender sender, @Argument(value = "world") String worldName, - @Argument(value = "data-source") NamedSlimeLoader loader) { + @Argument(value = "data-source") com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader loader) { if (commandManager.getWorldsInUse().contains(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already being used on another command! Wait some time and try again.")).color(NamedTextColor.RED) ); } @@ -51,15 +43,15 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value World world = Bukkit.getWorld(worldName); if (world != null) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " already exists!")).color(NamedTextColor.RED) ); } - WorldsConfig config = ConfigManager.getWorldConfig(); + com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); if (config.getWorlds().containsKey(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("There is already a world called " + worldName + " inside the worlds config file.")).color(NamedTextColor.RED) ); } @@ -81,7 +73,7 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value throw new WorldAlreadyExistsException("World already exists"); } - WorldData worldData = new WorldData(); + com.infernalsuite.asp.plugin.config.WorldData worldData = new com.infernalsuite.asp.plugin.config.WorldData(); worldData.setSpawn("0, 64, 0"); worldData.setDataSource(loader.name()); @@ -89,7 +81,7 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value SlimeWorld slimeWorld = asp.createEmptyWorld(worldName, false, propertyMap, loader.slimeLoader()); asp.saveWorld(slimeWorld); - ExecutorUtil.runSyncAndWait(plugin, () -> { + com.infernalsuite.asp.plugin.util.ExecutorUtil.runSyncAndWait(plugin, () -> { try { asp.loadWorld(slimeWorld, true); @@ -100,7 +92,7 @@ public CompletableFuture createWorld(CommandSender sender, @Argument(value // Config config.getWorlds().put(worldName, worldData); } catch (IllegalArgumentException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to create world " + worldName + ": " + ex.getMessage() + ".").color(NamedTextColor.RED) )); } @@ -113,12 +105,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 MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.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 MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.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/aswm/plugin/commands/sub/DSListCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DSListCmd.java similarity index 78% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/DSListCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DSListCmd.java index 4c7dbf5a3..e00ca549f 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/DSListCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DSListCmd.java @@ -1,12 +1,8 @@ -package com.infernalsuite.aswm.plugin.commands.sub; - -import com.infernalsuite.aswm.api.SlimeNMSBridge; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; +package com.infernalsuite.asp.plugin.commands.sub; + +import com.infernalsuite.asp.api.SlimeNMSBridge; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -20,24 +16,24 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -public class DSListCmd extends SlimeCommand { +public class DSListCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final int MAX_ITEMS_PER_PAGE = 5; private static final Logger LOGGER = LoggerFactory.getLogger(DSListCmd.class); - public DSListCmd(CommandManager commandManager) { + public DSListCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @Command("swp|aswm|swm dslist [page]") @CommandDescription("List all worlds inside a data source.") @Permission("swm.dslist") - public CompletableFuture listWorlds(CommandSender sender, @Argument(value = "data-source") NamedSlimeLoader namedLoader, + public CompletableFuture listWorlds(CommandSender sender, @Argument(value = "data-source") com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader namedLoader, @Default("1") @Argument(value = "page") int page) { SlimeLoader loader = namedLoader.slimeLoader(); if (page < 1) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Page number must be greater than 0!").color(NamedTextColor.RED) )); } @@ -50,13 +46,13 @@ public CompletableFuture listWorlds(CommandSender sender, @Argument(value worldList = loader.listWorlds(); } catch (IOException ex) { LOGGER.error("Failed to load world list:", ex); - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to load world list. Take a look at the server console for more information.").color(NamedTextColor.RED) )); } if (worldList.isEmpty()) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("There are no worlds stored in data source " + namedLoader.name() + ".").color(NamedTextColor.RED) )); } @@ -66,7 +62,7 @@ public CompletableFuture listWorlds(CommandSender sender, @Argument(value int maxPages = ((int) d) + ((d > (int) d) ? 1 : 0); if (offset >= worldList.size()) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("There " + (maxPages == 1 ? "is" : "are") + " only " + maxPages + " page" + (maxPages == 1 ? "" : "s") + "!").color(NamedTextColor.RED) )); @@ -95,7 +91,7 @@ private boolean isLoaded(SlimeLoader loader, String worldName) { World world = Bukkit.getWorld(worldName); if (world != null) { - SlimeWorld slimeWorld = SlimeNMSBridge.instance().getInstance(world).getSlimeWorldMirror(); + SlimeWorld slimeWorld = SlimeNMSBridge.instance().getInstance(world); if (slimeWorld != null) { return loader.equals(slimeWorld.getLoader()); diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/DeleteWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DeleteWorldCmd.java similarity index 79% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/DeleteWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DeleteWorldCmd.java index a94a75494..12c9359ef 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/DeleteWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/DeleteWorldCmd.java @@ -1,16 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -30,11 +23,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class DeleteWorldCmd extends SlimeCommand { +public class DeleteWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(DeleteWorldCmd.class); - public DeleteWorldCmd(CommandManager commandManager) { + public DeleteWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @@ -46,11 +39,11 @@ public DeleteWorldCmd(CommandManager commandManager) { @RawArgs public CompletableFuture deleteWorld(CommandSender sender, String[] args, @Argument(value = "world", suggestions = "known-slime-worlds") String worldName, - @Argument(value = "data-source") @Nullable NamedSlimeLoader dataSource) { + @Argument(value = "data-source") @Nullable com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader dataSource) { World world = Bukkit.getWorld(worldName); if (world != null) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is loaded on this server! Unload it by running the command ").color(NamedTextColor.RED) .append(Component.text("/swm unload " + worldName).color(NamedTextColor.GRAY)) .append(Component.text(".")).color(NamedTextColor.RED) @@ -62,11 +55,11 @@ public CompletableFuture deleteWorld(CommandSender sender, String[] args, if (dataSource != null) { loader = dataSource.slimeLoader(); } else { - WorldsConfig config = ConfigManager.getWorldConfig(); - WorldData worldData = config.getWorlds().get(worldName); + com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); + com.infernalsuite.asp.plugin.config.WorldData worldData = config.getWorlds().get(worldName); if (worldData == null) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to find world " + worldName + " inside the worlds config file!").color(NamedTextColor.RED) )); } @@ -76,13 +69,13 @@ public CompletableFuture deleteWorld(CommandSender sender, String[] args, if (loader == null) { // This could happen if the loader inside WorldData is invalid - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Unknown data source! Are you sure you typed it correctly?").color(NamedTextColor.RED) )); } if (commandManager.getWorldsInUse().contains(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already being used on another command! Wait some time and try again.").color(NamedTextColor.RED) )); } @@ -107,7 +100,7 @@ public CompletableFuture deleteWorld(CommandSender sender, String[] args, loader.deleteWorld(worldName); // Now let's delete it from the config file - WorldsConfig config = ConfigManager.getWorldConfig(); + com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); config.getWorlds().remove(worldName); config.save(); @@ -120,11 +113,11 @@ public CompletableFuture deleteWorld(CommandSender sender, String[] args, } catch (IOException ex) { LOGGER.error("Failed to delete world {}", worldName, ex); - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to delete world " + worldName + ". Take a look at the server console for more information.").color(NamedTextColor.RED) )); } catch (UnknownWorldException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Data source " + loader + " does not contain any world called " + worldName + ".").color(NamedTextColor.RED) )); } finally { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/GotoCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/GotoCmd.java similarity index 78% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/GotoCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/GotoCmd.java index 5dad6ccc7..d6dd7f967 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/GotoCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/GotoCmd.java @@ -1,10 +1,5 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.config.ConfigManager; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -18,9 +13,9 @@ import org.incendo.cloud.annotations.Permission; import org.jetbrains.annotations.Nullable; -public class GotoCmd extends SlimeCommand { +public class GotoCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { - public GotoCmd(CommandManager commandManager) { + public GotoCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @@ -33,7 +28,7 @@ public void onCommand(CommandSender sender, @Argument(value = "world") World wor if (target == null) { if (!(sender instanceof Player)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("The console cannot be teleported to a world! Please specify a player.").color(NamedTextColor.RED) )); } @@ -60,8 +55,8 @@ public void onCommand(CommandSender sender, @Argument(value = "world") World wor } Location spawnLocation; - if (ConfigManager.getWorldConfig().getWorlds().containsKey(world.getName())) { - String spawn = ConfigManager.getWorldConfig().getWorlds().get(world.getName()).getSpawn(); + if (com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig().getWorlds().containsKey(world.getName())) { + String spawn = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig().getWorlds().get(world.getName()).getSpawn(); String[] coords = spawn.split(", "); double x = Double.parseDouble(coords[0]); double y = Double.parseDouble(coords[1]); diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/HelpCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/HelpCmd.java similarity index 73% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/HelpCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/HelpCmd.java index 89c72d011..f60c2f11f 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/HelpCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/HelpCmd.java @@ -1,7 +1,5 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; import org.bukkit.command.CommandSender; import org.incendo.cloud.annotations.Argument; import org.incendo.cloud.annotations.Command; @@ -11,11 +9,11 @@ import org.incendo.cloud.paper.PaperCommandManager; import org.jetbrains.annotations.Nullable; -public class HelpCmd extends SlimeCommand { +public class HelpCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private final MinecraftHelp help; - public HelpCmd(CommandManager commandManager, LegacyPaperCommandManager cloudCommandManager) { + public HelpCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager, LegacyPaperCommandManager cloudCommandManager) { super(commandManager); this.help = MinecraftHelp.createNative("/swp help", cloudCommandManager); } diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ImportWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ImportWorldCmd.java similarity index 75% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ImportWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ImportWorldCmd.java index e3d3ef012..e4f463378 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ImportWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ImportWorldCmd.java @@ -1,21 +1,13 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.infernalsuite.aswm.api.exceptions.InvalidWorldException; -import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -import com.infernalsuite.aswm.api.exceptions.WorldLoadedException; -import com.infernalsuite.aswm.api.exceptions.WorldTooBigException; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.api.world.properties.SlimeProperties; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; -import com.infernalsuite.aswm.plugin.util.ExecutorUtil; +import com.infernalsuite.asp.api.exceptions.InvalidWorldException; +import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; +import com.infernalsuite.asp.api.exceptions.WorldLoadedException; +import com.infernalsuite.asp.api.exceptions.WorldTooBigException; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.api.world.properties.SlimeProperties; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; @@ -33,11 +25,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class ImportWorldCmd extends SlimeCommand { +public class ImportWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(ImportWorldCmd.class); - public ImportWorldCmd(CommandManager commandManager) { + public ImportWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @@ -49,12 +41,12 @@ public ImportWorldCmd(CommandManager commandManager) { @RawArgs public CompletableFuture importWorld(CommandSender sender, String[] args, @Argument(value = "path-to-world") String pathToWorld, - @Argument(value = "data-source") NamedSlimeLoader loader, + @Argument(value = "data-source") com.infernalsuite.asp.plugin.commands.parser.NamedSlimeLoader loader, @Argument(value = "new-world-name") String newWorldName) { File worldDir = new File(pathToWorld); if (!worldDir.exists() || !worldDir.isDirectory()) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Path " + worldDir.getPath() + " does not point out to a valid world directory.")).color(NamedTextColor.RED) ); } @@ -70,10 +62,10 @@ public CompletableFuture importWorld(CommandSender sender, String[] args, Component.text("Importing world " + worldDir.getName() + " into data source " + loader.slimeLoader() + "...")) ); - WorldsConfig config = ConfigManager.getWorldConfig(); + com.infernalsuite.asp.plugin.config.WorldsConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getWorldConfig(); if (config.getWorlds().containsKey(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("There is already a world called " + worldName + " inside the worlds config file.")).color(NamedTextColor.RED) ); } @@ -84,7 +76,7 @@ public CompletableFuture importWorld(CommandSender sender, String[] args, SlimeWorld world = asp.readVanillaWorld(worldDir, worldName, loader.slimeLoader()); asp.saveWorld(world); - ExecutorUtil.runSyncAndWait(plugin, () -> { + com.infernalsuite.asp.plugin.util.ExecutorUtil.runSyncAndWait(plugin, () -> { asp.loadWorld(world, true); }); @@ -94,15 +86,10 @@ public CompletableFuture importWorld(CommandSender sender, String[] args, .append(Component.text(" imported successfully in " + (System.currentTimeMillis() - start) + "ms.")).color(NamedTextColor.GREEN) )); - WorldData worldData = new WorldData(); - StringBuilder spawn = new StringBuilder(); + com.infernalsuite.asp.plugin.config.WorldData worldData = new com.infernalsuite.asp.plugin.config.WorldData(); + for (String key : world.getPropertyMap().getProperties().keySet()) { switch (key.toLowerCase()) { - case "spawnx" -> - spawn.append(world.getPropertyMap().getValue(SlimeProperties.SPAWN_X)).append(", "); - case "spawny" -> - spawn.append(world.getPropertyMap().getValue(SlimeProperties.SPAWN_Y)).append(", "); - case "spawnz" -> spawn.append(world.getPropertyMap().getValue(SlimeProperties.SPAWN_Z)); case "environment" -> worldData.setEnvironment(world.getPropertyMap().getValue(SlimeProperties.ENVIRONMENT)); case "difficulty" -> @@ -120,30 +107,34 @@ public CompletableFuture importWorld(CommandSender sender, String[] args, } worldData.setDataSource(loader.name()); - worldData.setSpawn(spawn.toString().isEmpty() ? "0.5, 255, 0.5" : spawn.toString()); + worldData.setSpawn( + world.getPropertyMap().getValue(SlimeProperties.SPAWN_X) + ", " + + world.getPropertyMap().getValue(SlimeProperties.SPAWN_Y) + ", " + + world.getPropertyMap().getValue(SlimeProperties.SPAWN_Z) + ); config.getWorlds().put(worldName, worldData); config.save(); } catch (WorldAlreadyExistsException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Data source " + loader + " already contains a world called " + worldName + ".")).color(NamedTextColor.RED) ); } catch (InvalidWorldException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Directory " + worldDir.getName() + " does not contain a valid Minecraft world.")).color(NamedTextColor.RED) ); } catch (WorldLoadedException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldDir.getName() + " is loaded on this server. Please unload it before importing it.")).color(NamedTextColor.RED) ); } catch (WorldTooBigException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Hey! Didn't you just read the warning? The Slime Format isn't meant for big worlds." + " The world you provided just breaks everything. Please, trim it by using the MCEdit tool and try again.")).color(NamedTextColor.RED) ); } catch (IOException ex) { LOGGER.error("Failed to import world {}:", worldDir.getName(), ex); - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to import world " + worldName + ". Take a look at the server console for more information.")).color(NamedTextColor.RED) ); } diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadTemplateWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadTemplateWorldCmd.java similarity index 82% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadTemplateWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadTemplateWorldCmd.java index 66a57fbd4..5cb1f3cff 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadTemplateWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadTemplateWorldCmd.java @@ -1,16 +1,11 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedWorldData; -import com.infernalsuite.aswm.plugin.util.ExecutorUtil; +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -26,35 +21,35 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; -public class LoadTemplateWorldCmd extends SlimeCommand { +public class LoadTemplateWorldCmd extends com.infernalsuite.asp.plugin.commands.SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(LoadTemplateWorldCmd.class); - public LoadTemplateWorldCmd(CommandManager commandManager) { + public LoadTemplateWorldCmd(com.infernalsuite.asp.plugin.commands.CommandManager commandManager) { super(commandManager); } @Command("swp|aswm|swm load-template ") @CommandDescription("Creates a temporary world using another as a template. This world will never be stored.") @Permission("swm.loadworld.template") - public CompletableFuture onCommand(CommandSender sender, @Argument(value = "template-world") NamedWorldData templateWorldData, + public CompletableFuture onCommand(CommandSender sender, @Argument(value = "template-world") com.infernalsuite.asp.plugin.commands.parser.NamedWorldData templateWorldData, @Argument(value = "world-name") String worldName) { World world = Bukkit.getWorld(worldName); if (world != null) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already loaded!").color(NamedTextColor.RED) )); } if (templateWorldData.name().equals(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("The template world name cannot be the same as the cloned world one!").color(NamedTextColor.RED) )); } if (commandManager.getWorldsInUse().contains(worldName)) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("World " + worldName + " is already being used on another command! Wait some time and try again.").color(NamedTextColor.RED) )); } @@ -81,11 +76,11 @@ public CompletableFuture onCommand(CommandSender sender, @Argument(value = SlimeWorld templateWorld = getWorldReadyForCloning(templateWorldData.name(), loader, templateWorldData.worldData().toPropertyMap()); SlimeWorld slimeWorld = templateWorld.clone(worldName); - ExecutorUtil.runSyncAndWait(plugin, () -> { + com.infernalsuite.asp.plugin.util.ExecutorUtil.runSyncAndWait(plugin, () -> { try { asp.loadWorld(slimeWorld, true); } catch (IllegalArgumentException ex) { - throw new MessageCommandException(COMMAND_PREFIX.append( + throw new com.infernalsuite.asp.plugin.commands.exception.MessageCommandException(COMMAND_PREFIX.append( Component.text("Failed to generate world " + worldName + ": " + ex.getMessage() + ".").color(NamedTextColor.RED) )); } diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadWorldCmd.java similarity index 89% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadWorldCmd.java index c8500506d..12f29e123 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/LoadWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/LoadWorldCmd.java @@ -1,16 +1,16 @@ -package com.infernalsuite.aswm.plugin.commands.sub; - - -import com.infernalsuite.aswm.api.exceptions.CorruptedWorldException; -import com.infernalsuite.aswm.api.exceptions.NewerFormatException; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedWorldData; -import com.infernalsuite.aswm.plugin.util.ExecutorUtil; +package com.infernalsuite.asp.plugin.commands.sub; + + +import com.infernalsuite.asp.api.exceptions.CorruptedWorldException; +import com.infernalsuite.asp.api.exceptions.NewerFormatException; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.world.SlimeWorld; +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.NamedWorldData; +import com.infernalsuite.asp.plugin.util.ExecutorUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/MigrateWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/MigrateWorldCmd.java similarity index 87% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/MigrateWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/MigrateWorldCmd.java index 06a99cd38..f76c56bc7 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/MigrateWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/MigrateWorldCmd.java @@ -1,14 +1,14 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.exceptions.UnknownWorldException; -import com.infernalsuite.aswm.api.exceptions.WorldAlreadyExistsException; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.commands.parser.NamedSlimeLoader; -import com.infernalsuite.aswm.plugin.commands.parser.NamedWorldData; -import com.infernalsuite.aswm.plugin.config.ConfigManager; +import com.infernalsuite.asp.api.exceptions.UnknownWorldException; +import com.infernalsuite.asp.api.exceptions.WorldAlreadyExistsException; +import com.infernalsuite.asp.api.loaders.SlimeLoader; +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.commands.parser.NamedWorldData; +import com.infernalsuite.asp.plugin.config.ConfigManager; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ReloadConfigCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ReloadConfigCmd.java similarity index 81% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ReloadConfigCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ReloadConfigCmd.java index 261bf16fe..1ebd8d7ec 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/ReloadConfigCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/ReloadConfigCmd.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.config.ConfigManager; +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.config.ConfigManager; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.ChatColor; @@ -16,8 +16,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.concurrent.CompletableFuture; public class ReloadConfigCmd extends SlimeCommand { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SaveWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SaveWorldCmd.java similarity index 79% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SaveWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SaveWorldCmd.java index 38b82ab1a..5f55c5e72 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SaveWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SaveWorldCmd.java @@ -1,10 +1,10 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.plugin.commands.CommandManager; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -19,9 +19,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; public class SaveWorldCmd extends SlimeCommand { private static final Logger LOGGER = LoggerFactory.getLogger(SaveWorldCmd.class); diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SetSpawnCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SetSpawnCmd.java similarity index 81% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SetSpawnCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SetSpawnCmd.java index 38b0f5fe1..98b2f1023 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/SetSpawnCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/SetSpawnCmd.java @@ -1,13 +1,11 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.WorldData; -import com.infernalsuite.aswm.plugin.config.WorldsConfig; +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.config.ConfigManager; +import com.infernalsuite.asp.plugin.config.WorldData; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -20,10 +18,6 @@ import org.incendo.cloud.annotations.CommandDescription; import org.incendo.cloud.annotations.Permission; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - public class SetSpawnCmd extends SlimeCommand { public SetSpawnCmd(CommandManager commandManager) { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/UnloadWorldCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/UnloadWorldCmd.java similarity index 92% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/UnloadWorldCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/UnloadWorldCmd.java index f7717bd41..dfd1d2703 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/UnloadWorldCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/UnloadWorldCmd.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import com.infernalsuite.aswm.plugin.SWPlugin; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.api.world.SlimeWorld; +import com.infernalsuite.asp.plugin.SWPlugin; +import com.infernalsuite.asp.plugin.commands.CommandManager; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/VersionCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/VersionCmd.java similarity index 80% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/VersionCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/VersionCmd.java index 69e7bfdb0..83e40b455 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/VersionCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/VersionCmd.java @@ -1,9 +1,9 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.plugin.SWPlugin; -import com.infernalsuite.aswm.api.utils.SlimeFormat; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.SWPlugin; +import com.infernalsuite.asp.api.utils.SlimeFormat; +import com.infernalsuite.asp.plugin.commands.CommandManager; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/WorldListCmd.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/WorldListCmd.java similarity index 92% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/WorldListCmd.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/WorldListCmd.java index 49d70a48d..2896dbfc0 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/sub/WorldListCmd.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/commands/sub/WorldListCmd.java @@ -1,10 +1,10 @@ -package com.infernalsuite.aswm.plugin.commands.sub; +package com.infernalsuite.asp.plugin.commands.sub; -import com.infernalsuite.aswm.plugin.commands.SlimeCommand; -import com.infernalsuite.aswm.plugin.commands.CommandManager; -import com.infernalsuite.aswm.plugin.commands.exception.MessageCommandException; -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.api.SlimeNMSBridge; +import com.infernalsuite.asp.plugin.commands.SlimeCommand; +import com.infernalsuite.asp.plugin.commands.CommandManager; +import com.infernalsuite.asp.plugin.commands.exception.MessageCommandException; +import com.infernalsuite.asp.plugin.config.ConfigManager; +import com.infernalsuite.asp.api.SlimeNMSBridge; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/ConfigManager.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/ConfigManager.java similarity index 96% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/config/ConfigManager.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/config/ConfigManager.java index f90dd35c2..6dba89baa 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/ConfigManager.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/ConfigManager.java @@ -1,6 +1,6 @@ -package com.infernalsuite.aswm.plugin.config; +package com.infernalsuite.asp.plugin.config; -import com.infernalsuite.aswm.plugin.SWPlugin; +import com.infernalsuite.asp.plugin.SWPlugin; import io.leangen.geantyref.TypeToken; import org.spongepowered.configurate.loader.HeaderMode; import org.spongepowered.configurate.yaml.NodeStyle; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/DatasourcesConfig.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/DatasourcesConfig.java similarity index 99% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/config/DatasourcesConfig.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/config/DatasourcesConfig.java index f28a6f8b4..1b8e06777 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/DatasourcesConfig.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/DatasourcesConfig.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.plugin.config; +package com.infernalsuite.asp.plugin.config; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Setting; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldData.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java similarity index 96% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldData.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java index 30dd9feb5..6ce08da9f 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldData.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldData.java @@ -1,12 +1,12 @@ -package com.infernalsuite.aswm.plugin.config; +package com.infernalsuite.asp.plugin.config; -import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap; +import com.infernalsuite.asp.api.world.properties.SlimePropertyMap; import org.bukkit.Difficulty; import org.bukkit.World; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Setting; -import static com.infernalsuite.aswm.api.world.properties.SlimeProperties.*; +import static com.infernalsuite.asp.api.world.properties.SlimeProperties.*; @ConfigSerializable public class WorldData { diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldsConfig.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldsConfig.java similarity index 95% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldsConfig.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldsConfig.java index 8d65ab38f..1b429741b 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/config/WorldsConfig.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/config/WorldsConfig.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.plugin.config; +package com.infernalsuite.asp.plugin.config; import io.leangen.geantyref.TypeToken; import org.slf4j.Logger; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/loader/LoaderManager.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java similarity index 71% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/loader/LoaderManager.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java index 73b35b25d..ccf09ad7e 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/loader/LoaderManager.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/loader/LoaderManager.java @@ -1,14 +1,12 @@ -package com.infernalsuite.aswm.plugin.loader; - -import com.infernalsuite.aswm.plugin.config.ConfigManager; -import com.infernalsuite.aswm.plugin.config.DatasourcesConfig; -import com.infernalsuite.aswm.api.loaders.SlimeLoader; -import com.infernalsuite.aswm.loaders.UpdatableLoader; -import com.infernalsuite.aswm.loaders.api.APILoader; -import com.infernalsuite.aswm.loaders.file.FileLoader; -import com.infernalsuite.aswm.loaders.mongo.MongoLoader; -import com.infernalsuite.aswm.loaders.mysql.MysqlLoader; -import com.infernalsuite.aswm.loaders.redis.RedisLoader; +package com.infernalsuite.asp.plugin.loader; + +import com.infernalsuite.asp.api.loaders.SlimeLoader; +import com.infernalsuite.asp.api.loaders.UpdatableLoader; +import com.infernalsuite.asp.loaders.api.APILoader; +import com.infernalsuite.asp.loaders.file.FileLoader; +import com.infernalsuite.asp.loaders.mongo.MongoLoader; +import com.infernalsuite.asp.loaders.mysql.MysqlLoader; +import com.infernalsuite.asp.loaders.redis.RedisLoader; import com.mongodb.MongoException; import io.lettuce.core.RedisException; import org.slf4j.Logger; @@ -27,14 +25,14 @@ public class LoaderManager { private final Map loaders = new HashMap<>(); public LoaderManager() { - DatasourcesConfig config = ConfigManager.getDatasourcesConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig config = com.infernalsuite.asp.plugin.config.ConfigManager.getDatasourcesConfig(); // File loader - DatasourcesConfig.FileConfig fileConfig = config.getFileConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig.FileConfig fileConfig = config.getFileConfig(); registerLoader("file", new FileLoader(new File(fileConfig.getPath()))); // Mysql loader - DatasourcesConfig.MysqlConfig mysqlConfig = config.getMysqlConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig.MysqlConfig mysqlConfig = config.getMysqlConfig(); if (mysqlConfig.isEnabled()) { try { registerLoader("mysql", new MysqlLoader( @@ -49,7 +47,7 @@ public LoaderManager() { } // MongoDB loader - DatasourcesConfig.MongoDBConfig mongoConfig = config.getMongoDbConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig.MongoDBConfig mongoConfig = config.getMongoDbConfig(); if (mongoConfig.isEnabled()) { try { @@ -68,7 +66,7 @@ public LoaderManager() { } } - DatasourcesConfig.RedisConfig redisConfig = config.getRedisConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig.RedisConfig redisConfig = config.getRedisConfig(); if (redisConfig.isEnabled()){ try { registerLoader("redis", new RedisLoader(redisConfig.getUri())); @@ -77,7 +75,7 @@ public LoaderManager() { } } - DatasourcesConfig.APIConfig apiConfig = config.getApiConfig(); + com.infernalsuite.asp.plugin.config.DatasourcesConfig.APIConfig apiConfig = config.getApiConfig(); if(apiConfig.isEnabled()){ registerLoader("api", new APILoader( apiConfig.getUrl(), @@ -96,9 +94,9 @@ public void registerLoader(String dataSource, SlimeLoader loader) { if (loader instanceof UpdatableLoader) { try { ((UpdatableLoader) loader).update(); - } catch (final UpdatableLoader.NewerDatabaseException e) { - LOGGER.error("Data source {} version is {}, while this SWM version only supports up to version {}.", - dataSource, e.getDatabaseVersion(), e.getCurrentVersion(), e); + } catch (final UpdatableLoader.NewerStorageException e) { + LOGGER.error("Data source {} version is {}, while this loader version only supports up to version {}.", + dataSource, e.getStorageVersion(), e.getImplementationVersion(), e); return; } catch (final IOException ex) { LOGGER.error("Failed to update data source {}", dataSource, ex); diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/util/ExecutorUtil.java b/plugin/src/main/java/com/infernalsuite/asp/plugin/util/ExecutorUtil.java similarity index 96% rename from plugin/src/main/java/com/infernalsuite/aswm/plugin/util/ExecutorUtil.java rename to plugin/src/main/java/com/infernalsuite/asp/plugin/util/ExecutorUtil.java index 95fe092b3..7e4fea235 100644 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/util/ExecutorUtil.java +++ b/plugin/src/main/java/com/infernalsuite/asp/plugin/util/ExecutorUtil.java @@ -1,4 +1,4 @@ -package com.infernalsuite.aswm.plugin.util; +package com.infernalsuite.asp.plugin.util; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; diff --git a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldData.java b/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldData.java deleted file mode 100644 index 35d03d2f9..000000000 --- a/plugin/src/main/java/com/infernalsuite/aswm/plugin/commands/parser/NamedWorldData.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.infernalsuite.aswm.plugin.commands.parser; - -import com.infernalsuite.aswm.plugin.config.WorldData; - -public record NamedWorldData(String name, WorldData worldData) { -} diff --git a/plugin/src/main/resources/sources.yml b/plugin/src/main/resources/sources.yml index 0a9499065..c15b59514 100644 --- a/plugin/src/main/resources/sources.yml +++ b/plugin/src/main/resources/sources.yml @@ -1,5 +1,5 @@ # Inside this file is the configuration options -# for the data sources that SWM supports +# for the data sources that ASP supports mysql: enabled: false host: 127.0.0.1 diff --git a/settings.gradle.kts b/settings.gradle.kts index 2fe280a22..ccd12612a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,13 +1,34 @@ pluginManagement { repositories { mavenLocal() + mavenCentral() gradlePluginPortal() maven("https://repo.papermc.io/repository/maven-public/") } } -rootProject.name = "slimeworldmanager" +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" +} + +rootProject.name = "ASPaper" + +include(":gradle:platform") +include(":api") +include(":core") +include(":importer") +include(":loaders") +include(":plugin") +include(":aspaper-api") +include(":aspaper-server") -include("plugin", "core", "api", "importer") -include("slimeworldmanager-api", "slimeworldmanager-server") -include("loaders") +include("loaders:mongo-loader") +findProject(":loaders:mongo-loader")?.name = "mongo-loader" +include("loaders:api-loader") +findProject(":loaders:api-loader")?.name = "api-loader" +include("loaders:file-loader") +findProject(":loaders:file-loader")?.name = "file-loader" +include("loaders:mysql-loader") +findProject(":loaders:mysql-loader")?.name = "mysql-loader" +include("loaders:redis-loader") +findProject(":loaders:redis-loader")?.name = "redis-loader"