diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f79450e4..e48b42a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ but does not necessarily have to. ## World Interaction -Load chunks asynchronously using PaperLib whenever possible. +Load chunks asynchronously whenever possible. ## Dependency Injection diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md index b834bceb..1f4176ff 100644 --- a/THIRD-PARTY.md +++ b/THIRD-PARTY.md @@ -1,4 +1,4 @@ -Lists of 29 third-party dependencies. +Lists of 28 third-party dependencies. - (Public Domain) [AOP alliance (aopalliance:aopalliance:1.0)](http://aopalliance.sourceforge.net) - (MIT) [DelegatingMap (co.aikar:DelegatingMap:1.0.0-20180331.052245-5)]() - (MIT) [Table (co.aikar:Table:1.0.0-20180331.054128-7)]() @@ -16,7 +16,6 @@ Lists of 29 third-party dependencies. - (ISC License) [ORMLite JDBC (com.j256.ormlite:ormlite-jdbc:5.1)](http://ormlite.sourceforge.net/) - (Apache License, Version 2.0) [config (com.typesafe:config:1.3.1)](https://github.com/typesafehub/config) - (The Apache Software License, Version 2.0) [Commons Lang (commons-lang:commons-lang:2.6)](http://commons.apache.org/lang/) -- (MIT) [PaperLib (io.papermc:paperlib:1.0.2)](https://github.com/PaperMC/PaperLib) - (The Apache Software License, Version 2.0) [javax.inject (javax.inject:javax.inject:1)](http://code.google.com/p/atinject/) - (MIT) [MiniMessage (me.minidigger:MiniMessage:1.0.1)](https://github.com/MiniDigger/MiniMessage) - (Apache License, Version 2.0) [ExpiringMap (net.jodah:expiringmap:0.5.8)](http://github.com/jhalterman/expiringmap/) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 658cffb7..637d409a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,18 +3,17 @@ cloud-build-logic = "0.0.3" ktlint = "0.50.0" checkstyle = "10.12.5" kotlin = "1.9.20" -shadow = "8.1.1" +shadow = "8.3.5" paperweight = "1.7.1" run-paper = "2.3.0" pluginyml = "0.6.0" # Platforms -minecraft = "1.17.1-R0.1-SNAPSHOT" +minecraft = "1.18.2-R0.1-SNAPSHOT" # Libraries cloud = "1.8.4" guice = "5.1.0" -paperlib = "1.0.8" taskchain = "3.7.2" bstats = "3.0.2" configurate = "4.1.2" @@ -38,7 +37,6 @@ cloudMinecraftExtras = { group = "cloud.commandframework", name = "cloud-minecra cloudAnnotations = { group = "cloud.commandframework", name = "cloud-annotations", version.ref = "cloud" } guice = { group = "com.google.inject", name = "guice", version.ref = "guice" } assistedInject = { group = "com.google.inject.extensions", name = "guice-assistedinject", version.ref = "guice" } -paperlib = { group = "io.papermc", name = "paperlib", version.ref = "paperlib"} taskchain = { group = "co.aikar", name = "taskchain-bukkit", version.ref = "taskchain" } bstats = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" } configurateHocon = { group = "org.spongepowered", name = "configurate-hocon", version.ref = "configurate"} @@ -54,6 +52,6 @@ paperweight-userdev = { id = "io.papermc.paperweight.userdev", version.ref = "pa cloud-buildLogic-spotless = { id = "org.incendo.cloud-build-logic.spotless", version.ref = "cloud-build-logic" } cloud-buildLogic-rootProject-publishing = { id = "org.incendo.cloud-build-logic.publishing.root-project", version.ref = "cloud-build-logic" } cloud-buildLogic-rootProject-spotless = { id = "org.incendo.cloud-build-logic.spotless.root-project", version.ref = "cloud-build-logic" } -shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } run-paper = { id = "xyz.jpenilla.run-paper", version.ref = "run-paper" } pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbf..d64cd491 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3499ded5..cea7a793 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68d..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/hyperverse-core/build.gradle.kts b/hyperverse-core/build.gradle.kts index 10cbea78..bfa6b616 100644 --- a/hyperverse-core/build.gradle.kts +++ b/hyperverse-core/build.gradle.kts @@ -26,7 +26,6 @@ dependencies { // TODO: Remove, because yuck. implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") implementation(libs.taskchain) - implementation(libs.paperlib) implementation(libs.guice) { exclude("com.google.guava", "guava") } @@ -37,16 +36,8 @@ dependencies { implementation(libs.configurateHocon) implementation(libs.cloudPaper) implementation(libs.cloudMinecraftExtras) - implementation(libs.cloudMinecraftExtras) - - // TODO: Remove and use native versions. - implementation("net.kyori:adventure-platform-bukkit:4.3.2") - implementation("net.kyori:adventure-text-minimessage:4.14.0") implementation(projects.hyperverseNmsUnsupported) - runtimeOnly(project(":hyperverse-nms-1-17")) { - targetConfiguration = "reobf" - } runtimeOnly(project(":hyperverse-nms-1-18")) { targetConfiguration = "reobf" } @@ -62,6 +53,12 @@ dependencies { runtimeOnly(project(":hyperverse-nms-1-21")) { targetConfiguration = "reobf" } + runtimeOnly(project(":hyperverse-nms-1-21-1")) { + targetConfiguration = "reobf" + } + runtimeOnly(project(":hyperverse-nms-1-21-3")) { + targetConfiguration = "reobf" + } } bukkit { @@ -70,7 +67,7 @@ bukkit { authors = listOf("Citymonstret", "andrewandy") main = "org.incendo.hyperverse.Hyperverse" softDepend = listOf("Essentials", "Multiverse", "MyWorlds") - apiVersion = "1.14" + apiVersion = "1.18" permissions { mapOf( "worlds" to "Allows players to use the Hyperverse command", @@ -117,12 +114,13 @@ tasks { shadowJar { minimize { exclude(project(":hyperverse-nms-unsupported")) - exclude(project(":hyperverse-nms-1-17")) exclude(project(":hyperverse-nms-1-18")) exclude(project(":hyperverse-nms-1-19")) exclude(project(":hyperverse-nms-1-20")) exclude(project(":hyperverse-nms-1-20-6")) exclude(project(":hyperverse-nms-1-21")) + exclude(project(":hyperverse-nms-1-21-1")) + exclude(project(":hyperverse-nms-1-21-3")) } mergeServiceFiles() @@ -132,14 +130,12 @@ tasks { } } - relocate("io.papermc.lib", "org.incendo.hyperverse.libs.paperlib") relocate("org.bstats", "org.incendo.hyperverse.libs.bstats") relocate("co.aikar.commands", "org.incendo.hyperverse.libs.aikar.commands") relocate("co.aikar.locales", "org.incendo.hyperverse.libs.aikar.locales") relocate("co.aikar.taskchain", "org.incendo.hyperverse.libs.taskchain") relocate("co.aikar.util", "org.incendo.hyperverse.libs.aikar.util") relocate("net.jodah.expiringmap", "org.incendo.hyperverse.libs.expiringmap") - relocate("net.kyori", "org.incendo.hyperverse.libs.kyori") relocate("cloud.commandframework", "org.incendo.hyperverse.libs.cloud") relocate("org.spongepowered.configurate", "org.incendo.hyperverse.libs.configurate") relocate("io.leangen.geantyref", "org.incendo.hyperverse.libs.geantyref") @@ -160,6 +156,6 @@ tasks { runServer { java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - minecraftVersion("1.21") + minecraftVersion("1.21.3") } } diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java index 944714b7..09ce8b42 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java @@ -24,7 +24,6 @@ import com.google.inject.Stage; import io.leangen.geantyref.GenericTypeReflector; import io.leangen.geantyref.TypeToken; -import io.papermc.lib.PaperLib; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -98,7 +97,9 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste Version.parseMinecraft("1.19.4"), Version.parseMinecraft("1.20.4"), Version.parseMinecraft("1.20.6"), - Version.parseMinecraft("1.21") + Version.parseMinecraft("1.21"), + Version.parseMinecraft("1.21.1"), + Version.parseMinecraft("1.21.3") ); private WorldManager worldManager; @@ -136,7 +137,7 @@ public void onEnable() { } Version currentMcVersion = VersionUtil.parseMinecraftVersion(Bukkit.getBukkitVersion()); if (!this.supportedVersions.contains(currentMcVersion)) { - throw new UnsupportedOperationException("Current mc version: " + currentMcVersion + "is not supported"); + throw new UnsupportedOperationException("Current mc version: " + currentMcVersion + " is not supported"); } PlatformProvider platformProvider = new ReflectionPlatformProvider(currentMcVersion); try { @@ -235,10 +236,6 @@ public void onEnable() { // Initialize bStats metrics tracking new Metrics(this, BSTATS_ID); - - // Add paper suggestion - PaperLib.suggestPaper(this); - } @Override diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/WorldFlagContainer.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/WorldFlagContainer.java index 57922d6f..2f64e18a 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/WorldFlagContainer.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/WorldFlagContainer.java @@ -97,7 +97,7 @@ public final void setParentContainer(final @NonNull FlagContainer parentContaine @Override public final @NonNull Map, WorldFlag> getFlagMap() { - return ImmutableMap., WorldFlag>builder().putAll(this.flagMap).build(); + return ImmutableMap.copyOf(this.flagMap); } @Override @@ -155,8 +155,9 @@ public final void clearLocal() { @Override public final @NonNull FlagContainer getHighestClassContainer() { - if (this.getParentContainer() != null) { - return this.getParentContainer(); + FlagContainer parentContainer = this.getParentContainer(); + if (parentContainer != null) { + return parentContainer; } return this; } @@ -173,8 +174,9 @@ public final void clearLocal() { if (flag != null) { return flag; } else { - if (this.getParentContainer() != null) { - return this.getParentContainer().getFlagErased(flagClass); + FlagContainer parentContainer = this.getParentContainer(); + if (parentContainer != null) { + return parentContainer.getFlagErased(flagClass); } } return null; @@ -196,8 +198,9 @@ public final void clearLocal() { if (flag != null) { return castUnsafe(flag); } else { - if (this.getParentContainer() != null) { - return this.getParentContainer().getFlag(flagClass); + FlagContainer parentContainer = this.getParentContainer(); + if (parentContainer != null) { + return parentContainer.getFlag(flagClass); } } return null; diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/DifficultyFlag.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/DifficultyFlag.java index 153c4624..0102fdb1 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/DifficultyFlag.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/DifficultyFlag.java @@ -39,20 +39,16 @@ private DifficultyFlag(final @NonNull Difficulty difficulty) { @Override public DifficultyFlag parse(final @NonNull String input) throws FlagParseException { - switch (input.toLowerCase()) { - case "peaceful": - return this.flagOf(Difficulty.PEACEFUL); - case "easy": - return this.flagOf(Difficulty.EASY); - case "normal": - return this.flagOf(Difficulty.NORMAL); - case "hard": - return this.flagOf(Difficulty.HARD); - default: - throw new FlagParseException(this, input, - "Invalid difficulty. Available values are: peaceful, easy, normal and hard" - ); - } + return switch (input.toLowerCase()) { + case "peaceful" -> this.flagOf(Difficulty.PEACEFUL); + case "easy" -> this.flagOf(Difficulty.EASY); + case "normal" -> this.flagOf(Difficulty.NORMAL); + case "hard" -> this.flagOf(Difficulty.HARD); + default -> throw new FlagParseException( + this, input, + "Invalid difficulty. Available values are: peaceful, easy, normal and hard" + ); + }; } @Override @@ -72,16 +68,12 @@ public String getExample() { @Override protected DifficultyFlag flagOf(final @NonNull Difficulty value) { - switch (value) { - case PEACEFUL: - return DIFFICULTY_FLAG_PEACEFUL; - case EASY: - return DIFFICULTY_FLAG_EASY; - case HARD: - return DIFFICULTY_FLAG_HARD; - default: - return DIFFICULTY_FLAG_NORMAL; - } + return switch (value) { + case PEACEFUL -> DIFFICULTY_FLAG_PEACEFUL; + case EASY -> DIFFICULTY_FLAG_EASY; + case HARD -> DIFFICULTY_FLAG_HARD; + default -> DIFFICULTY_FLAG_NORMAL; + }; } @Override diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/GamemodeFlag.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/GamemodeFlag.java index f08490e4..a5501d46 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/GamemodeFlag.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/flags/implementation/GamemodeFlag.java @@ -39,25 +39,13 @@ private GamemodeFlag(final @NonNull GameMode value) { @Override public GamemodeFlag parse(final @NonNull String input) throws FlagParseException { - switch (input.toLowerCase()) { - case "survival": - case "0": - case "s": - return GAMEMODE_SURVIVAL; - case "creative": - case "1": - case "c": - return GAMEMODE_CREATIVE; - case "adventure": - case "2": - case "a": - return GAMEMODE_ADVENTURE; - case "spectator": - case "3": - return GAMEMODE_SPECTATOR; - default: - throw new FlagParseException(this, input, "There is no such game mode"); - } + return switch (input.toLowerCase()) { + case "survival", "0", "s" -> GAMEMODE_SURVIVAL; + case "creative", "1", "c" -> GAMEMODE_CREATIVE; + case "adventure", "2", "a" -> GAMEMODE_ADVENTURE; + case "spectator", "3" -> GAMEMODE_SPECTATOR; + default -> throw new FlagParseException(this, input, "There is no such game mode"); + }; } @Override @@ -77,18 +65,12 @@ public String getExample() { @Override protected GamemodeFlag flagOf(final @NonNull GameMode value) { - switch (value) { - case SURVIVAL: - return GAMEMODE_SURVIVAL; - case CREATIVE: - return GAMEMODE_CREATIVE; - case ADVENTURE: - return GAMEMODE_ADVENTURE; - case SPECTATOR: - return GAMEMODE_SPECTATOR; - default: - throw new IllegalArgumentException("Unknown gamemode: " + value.name()); - } + return switch (value) { + case SURVIVAL -> GAMEMODE_SURVIVAL; + case CREATIVE -> GAMEMODE_CREATIVE; + case ADVENTURE -> GAMEMODE_ADVENTURE; + case SPECTATOR -> GAMEMODE_SPECTATOR; + }; } @Override diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/EventListener.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/EventListener.java index f6653a79..3c97c645 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/EventListener.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/EventListener.java @@ -17,9 +17,11 @@ package org.incendo.hyperverse.listeners; +import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; +import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent; +import com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import io.papermc.lib.PaperLib; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -71,6 +73,7 @@ import org.incendo.hyperverse.database.PersistentLocation; import org.incendo.hyperverse.events.PlayerSeekSpawnEvent; import org.incendo.hyperverse.events.PlayerSetSpawnEvent; +import org.incendo.hyperverse.flags.implementation.AdvancementFlag; import org.incendo.hyperverse.flags.implementation.CreatureSpawnFlag; import org.incendo.hyperverse.flags.implementation.EndFlag; import org.incendo.hyperverse.flags.implementation.GamemodeFlag; @@ -127,10 +130,6 @@ public EventListener( this.scheduler = scheduler; this.plugin = plugin; this.nms = nms; - // Register pre-spawn listeners - if (PaperLib.isPaper()) { - pluginManager.registerEvents(new PaperListener(this.worldManager), plugin); - } } /** @@ -167,7 +166,7 @@ public void onPlayerLogin(final @NonNull AsyncPlayerPreLoginEvent event) { } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onTeleport(final @NonNull PlayerTeleportEvent event) { if (this.hyperConfiguration.shouldPersistLocations()) { final Location from = event.getFrom(); @@ -201,7 +200,7 @@ public void onTeleport(final @NonNull PlayerTeleportEvent event) { } } this.nms.writePlayerData(event.getPlayer(), oldWorldDirectory.resolve( - String.format("%s.nbt", event.getPlayer().getUniqueId().toString()))); + String.format("%s.nbt", event.getPlayer().getUniqueId()))); } } } @@ -245,7 +244,7 @@ public void onWorldChange(final @NonNull PlayerChangedWorldEvent event) { } } final Path playerData = newWorldDirectory - .resolve(String.format("%s.nbt", player.getUniqueId().toString())); + .resolve(String.format("%s.nbt", player.getUniqueId())); if (Files.exists(playerData)) { final GameMode originalGameMode = player.getGameMode(); this.nms.readPlayerData(event.getPlayer(), playerData, @@ -357,7 +356,7 @@ public void onRespawn(final @NonNull PlayerRespawnEvent event) { event.setRespawnLocation(seekSpawnEvent.getRespawnLocation()); } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onEntityDamageEvent(final @NonNull EntityDamageByEntityEvent event) { final HyperWorld hyperWorld = this.worldManager.getWorld(event.getEntity().getWorld()); if (hyperWorld == null) { @@ -378,7 +377,7 @@ public void onEntityDamageEvent(final @NonNull EntityDamageByEntityEvent event) } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onPlayerPortalEvent(final @NonNull PlayerPortalEvent event) { final HyperWorld hyperWorld = this.worldManager.getWorld(event.getPlayer().getWorld()); if (hyperWorld == null) { @@ -472,14 +471,12 @@ public void onEntityPortalEnter(final @NonNull EntityPortalEnterEvent event) { if (location != null) { this.teleportationTimeout .put(event.getEntity().getUniqueId(), System.currentTimeMillis()); - PaperLib.teleportAsync(event.getEntity(), location, - PlayerTeleportEvent.TeleportCause.COMMAND - ); + event.getEntity().teleportAsync(location, PlayerTeleportEvent.TeleportCause.COMMAND); } else { this.plugin.getLogger().warning(String .format( "Failed to find/create a portal surrounding %s", - destination.toString() + destination )); } } @@ -497,14 +494,12 @@ public void onEntityPortalEnter(final @NonNull EntityPortalEnterEvent event) { final Location destination = hyperWorld.getTeleportationManager().endDestination(event.getEntity()); if (destination != null) { - PaperLib.teleportAsync(event.getEntity(), destination, - PlayerTeleportEvent.TeleportCause.COMMAND - ); + event.getEntity().teleportAsync(destination, PlayerTeleportEvent.TeleportCause.COMMAND); } } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onEntityPortalEvent(final @NonNull EntityPortalEvent event) { if (event.getEntityType() == EntityType.PLAYER) { return; @@ -525,21 +520,6 @@ public void onEntityPortalEvent(final @NonNull EntityPortalEvent event) { } } - @EventHandler - public void onEntityPreSpawn(final @NonNull CreatureSpawnEvent event) { - final HyperWorld hyperWorld = this.worldManager.getWorld(event.getLocation().getWorld()); - if (hyperWorld == null) { - return; - } - if (event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.NATURAL) { - return; - } - final Entity entity = event.getEntity(); - if (shouldCancelSpawn(hyperWorld, entity)) { - event.setCancelled(true); - } - } - @EventHandler public void onChunkGeneration(final @NonNull ChunkLoadEvent event) { if (!event.isNewChunk()) { @@ -585,4 +565,58 @@ public void onSleep(final @NonNull PlayerBedEnterEvent event) { ), true, false); } + @EventHandler + public void onEntityPreSpawn(final @NonNull PreCreatureSpawnEvent event) { + final HyperWorld hyperWorld = this.worldManager.getWorld(event.getSpawnLocation().getWorld()); + if (hyperWorld == null) { + return; + } + if (hyperWorld.getFlag(CreatureSpawnFlag.class)) { + return; + } + if (event.getReason() != CreatureSpawnEvent.SpawnReason.NATURAL) { + return; + } + event.setCancelled(true); + event.setShouldAbortSpawn(true); + } + + @EventHandler + public void onEntitySpawn(final @NonNull CreatureSpawnEvent event) { + final HyperWorld hyperWorld = this.worldManager.getWorld(event.getLocation().getWorld()); + if (hyperWorld == null) { + return; + } + if (event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.NATURAL) { + return; + } + final Entity entity = event.getEntity(); + if (shouldCancelSpawn(hyperWorld, entity)) { + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onMobPreSpawn(final @NonNull PlayerNaturallySpawnCreaturesEvent event) { + final HyperWorld hyperWorld = this.worldManager.getWorld(event.getPlayer().getWorld()); + if (hyperWorld == null) { + return; + } + if (hyperWorld.getFlag(MobSpawnFlag.class)) { + return; + } + event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = true) + public void onAdvancementGrant(final @NonNull PlayerAdvancementCriterionGrantEvent event) { + final HyperWorld hyperWorld = this.worldManager.getWorld(event.getPlayer().getWorld()); + if (hyperWorld == null) { + return; + } + if (hyperWorld.getFlag(AdvancementFlag.class)) { + return; + } + event.setCancelled(true); + } } diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/PaperListener.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/PaperListener.java deleted file mode 100644 index d6cefeac..00000000 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/listeners/PaperListener.java +++ /dev/null @@ -1,81 +0,0 @@ -// -// Hyperverse - A minecraft world management plugin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -package org.incendo.hyperverse.listeners; - -import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; -import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent; -import com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.incendo.hyperverse.flags.implementation.AdvancementFlag; -import org.incendo.hyperverse.flags.implementation.CreatureSpawnFlag; -import org.incendo.hyperverse.flags.implementation.MobSpawnFlag; -import org.incendo.hyperverse.world.HyperWorld; -import org.incendo.hyperverse.world.WorldManager; - -public final class PaperListener implements Listener { - - private final WorldManager worldManager; - - PaperListener(final @NonNull WorldManager worldManager) { - this.worldManager = worldManager; - } - - @EventHandler - public void onEntityPreSpawn(final @NonNull PreCreatureSpawnEvent event) { - final HyperWorld hyperWorld = this.worldManager.getWorld(event.getSpawnLocation().getWorld()); - if (hyperWorld == null) { - return; - } - if (hyperWorld.getFlag(CreatureSpawnFlag.class)) { - return; - } - if (event.getReason() != CreatureSpawnEvent.SpawnReason.NATURAL) { - return; - } - event.setCancelled(true); - event.setShouldAbortSpawn(true); - } - - @EventHandler - public void onMobPreSpawn(final @NonNull PlayerNaturallySpawnCreaturesEvent event) { - final HyperWorld hyperWorld = this.worldManager.getWorld(event.getPlayer().getWorld()); - if (hyperWorld == null) { - return; - } - if (hyperWorld.getFlag(MobSpawnFlag.class)) { - return; - } - event.setCancelled(true); - } - - @EventHandler - public void onAdvancementGrant(final @NonNull PlayerAdvancementCriterionGrantEvent event) { - final HyperWorld hyperWorld = this.worldManager.getWorld(event.getPlayer().getWorld()); - if (hyperWorld == null) { - return; - } - if (hyperWorld.getFlag(AdvancementFlag.class)) { - return; - } - event.setCancelled(true); - } - -} diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/teleportation/SimpleTeleportationManager.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/teleportation/SimpleTeleportationManager.java index 06e316b8..fefdb52d 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/teleportation/SimpleTeleportationManager.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/teleportation/SimpleTeleportationManager.java @@ -19,7 +19,6 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import io.papermc.lib.PaperLib; import org.bukkit.Location; import org.bukkit.block.BlockFace; import org.bukkit.block.data.type.Bed; @@ -102,7 +101,7 @@ private static boolean hasBedNearby(final @NonNull Location location) { final @NonNull Location location ) { if (this.configuration.shouldSafeTeleport()) { - return PaperLib.getChunkAtAsync(location).thenApply( + return location.getWorld().getChunkAtAsync(location).thenApply( chunk -> location.getBlock().getRelative(BlockFace.DOWN).getType().isSolid()); } else { return CompletableFuture.completedFuture(true); @@ -112,11 +111,11 @@ private static boolean hasBedNearby(final @NonNull Location location) { @Override public @NonNull CompletableFuture<@NonNull Location> findSafe(final @NonNull Location location) { if (this.configuration.shouldSafeTeleport()) { - return PaperLib.getChunkAtAsync(location).thenApply(chunk -> + return location.getWorld().getChunkAtAsync(location).thenApply(chunk -> this.hyperverse.getServicePipeline().pump(location).through(SafeTeleportService.class) .getResult()); } else { - return PaperLib.getChunkAtAsync(location).thenApply(c -> location); + return location.getWorld().getChunkAtAsync(location).thenApply(c -> location); } } @@ -125,8 +124,7 @@ public void teleportPlayer( final @NonNull Player player, final @NonNull Location location ) { - PaperLib.teleportAsync(player, Objects.requireNonNull(location)).thenAccept(l -> - player.setPortalCooldown(100)); + player.teleportAsync(location).thenAccept(l -> player.setPortalCooldown(100)); } @Override diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/IncendoPaster.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/IncendoPaster.java index 5f6efd48..cc30b647 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/IncendoPaster.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/IncendoPaster.java @@ -38,7 +38,6 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.stream.Collectors; /** * Single class paster for the Incendo paste service @@ -79,7 +78,7 @@ public IncendoPaster(final @NonNull String pasteApplication) { public static String readFile(final @NonNull File file) throws IOException { final List lines; try (final BufferedReader reader = new BufferedReader(new FileReader(file))) { - lines = reader.lines().collect(Collectors.toList()); + lines = reader.lines().toList(); } final StringBuilder content = new StringBuilder(); for (int i = Math.max(0, lines.size() - 1000); i < lines.size(); i++) { @@ -93,7 +92,7 @@ public static String readFile(final @NonNull File file) throws IOException { * * @return Unmodifiable collection */ - public final @NonNull Collection<@NonNull PasteFile> getFiles() { + public @NonNull Collection<@NonNull PasteFile> getFiles() { return Collections.unmodifiableCollection(this.files); } @@ -105,9 +104,9 @@ public static String readFile(final @NonNull File file) throws IOException { public void addFile(final @NonNull PasteFile file) { // Check to see that no duplicate files are submitted for (final PasteFile pasteFile : this.files) { - if (pasteFile.fileName.equalsIgnoreCase(file.getFileName())) { + if (pasteFile.fileName.equalsIgnoreCase(file.fileName())) { throw new IllegalArgumentException( - String.format("Found duplicate file with name %s", file.getFileName())); + String.format("Found duplicate file with name %s", file.fileName())); } } this.files.add(file); @@ -125,7 +124,7 @@ public void addFile(final @NonNull PasteFile file) { Iterator fileIterator = this.files.iterator(); while (fileIterator.hasNext()) { final PasteFile file = fileIterator.next(); - builder.append(file.getFileName()); + builder.append(file.fileName()); if (fileIterator.hasNext()) { builder.append(","); } @@ -134,8 +133,8 @@ public void addFile(final @NonNull PasteFile file) { fileIterator = this.files.iterator(); while (fileIterator.hasNext()) { final PasteFile file = fileIterator.next(); - builder.append("\"file-").append(file.getFileName()).append("\": \"") - .append(file.getContent().replaceAll("\"", "\\\\\"")).append("\""); + builder.append("\"file-").append(file.fileName()).append("\": \"") + .append(file.content().replaceAll("\"", "\\\\\"")).append("\""); if (fileIterator.hasNext()) { builder.append(",\n"); } @@ -150,7 +149,7 @@ public void addFile(final @NonNull PasteFile file) { * @return Status message * @throws Throwable any and all exceptions */ - public final @NonNull String upload() throws Throwable { + public @NonNull String upload() throws Throwable { final URL url = new URL(UPLOAD_PATH); final URLConnection connection = url.openConnection(); final HttpURLConnection httpURLConnection = (HttpURLConnection) connection; @@ -171,13 +170,14 @@ public void addFile(final @NonNull PasteFile file) { .warning(String.format("Paste Too Big > Size: %dMB", size / 1_000_000)); } throw new IllegalStateException(String - .format("Server returned status: %d %s", httpURLConnection.getResponseCode(), + .format( + "Server returned status: %d %s", httpURLConnection.getResponseCode(), httpURLConnection.getResponseMessage() )); } final StringBuilder input = new StringBuilder(); try (final BufferedReader inputStream = new BufferedReader( - new InputStreamReader(httpURLConnection.getInputStream(), StandardCharsets.UTF_8.name()))) { + new InputStreamReader(httpURLConnection.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = inputStream.readLine()) != null) { input.append(line).append("\n"); @@ -189,27 +189,19 @@ public void addFile(final @NonNull PasteFile file) { /** * Simple class that represents a paste file + * Construct a new paste file + * + * @param fileName File name, cannot be empty, nor null + * @param content File content, cannot be empty, nor null */ - public static final class PasteFile { - - private final String fileName; - private final String content; - - /** - * Construct a new paste file - * - * @param fileName File name, cannot be empty, nor null - * @param content File content, cannot be empty, nor null - */ - public PasteFile(final @NonNull String fileName, final @NonNull String content) { + public record PasteFile(@NonNull String fileName, @NonNull String content) { + public PasteFile { if (fileName.isEmpty()) { throw new IllegalArgumentException("file name cannot be null, nor empty"); } if (content.isEmpty()) { throw new IllegalArgumentException("content cannot be null, nor empty"); } - this.fileName = fileName; - this.content = content; } /** @@ -217,7 +209,8 @@ public PasteFile(final @NonNull String fileName, final @NonNull String content) * * @return File name */ - public @NonNull String getFileName() { + @Override + public @NonNull String fileName() { return this.fileName; } @@ -226,7 +219,8 @@ public PasteFile(final @NonNull String fileName, final @NonNull String content) * * @return File content */ - public @NonNull String getContent() { + @Override + public @NonNull String content() { return this.content; } diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/MessageUtil.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/MessageUtil.java index d20fee95..7d2e702d 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/MessageUtil.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/MessageUtil.java @@ -17,13 +17,10 @@ package org.incendo.hyperverse.util; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; -import org.incendo.hyperverse.Hyperverse; import org.incendo.hyperverse.configuration.Message; import org.incendo.hyperverse.configuration.Messages; @@ -34,7 +31,6 @@ */ public final class MessageUtil { - private static final BukkitAudiences AUDIENCES = BukkitAudiences.create(JavaPlugin.getPlugin(Hyperverse.class)); private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); private MessageUtil() { @@ -86,16 +82,16 @@ public static void sendMessage( if (replacedMessage.contains(ChatColor.COLOR_CHAR + "")) { prefixedMessage = ChatColor.translateAlternateColorCodes( '&', - Messages.messagePrefix.toString() + replacedMessage + Messages.messagePrefix + replacedMessage ); } else { - prefixedMessage = Messages.messagePrefixFancy.toString() + replacedMessage; + prefixedMessage = Messages.messagePrefixFancy + replacedMessage; } - AUDIENCES.sender(recipient).sendMessage(MINI_MESSAGE.deserialize(prefixedMessage)); + recipient.sendMessage(MINI_MESSAGE.deserialize(prefixedMessage)); } else { final String prefixedMessage = ChatColor.translateAlternateColorCodes( '&', - Messages.messagePrefix.toString() + replacedMessage + Messages.messagePrefix + replacedMessage ); recipient.sendMessage(prefixedMessage); } diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/world/WorldConfiguration.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/world/WorldConfiguration.java index 0af63dbd..5c8040d8 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/world/WorldConfiguration.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/world/WorldConfiguration.java @@ -264,7 +264,6 @@ public void writeToFile(final @NonNull Path path) { try (final BufferedWriter bufferedWriter = Files .newBufferedWriter(Objects.requireNonNull(path))) { GSON.toJson(this, WorldConfiguration.class, GSON.newJsonWriter(bufferedWriter)); - return; } catch (final Exception e) { e.printStackTrace(); } diff --git a/hyperverse-nms-1-18/src/main/java/org/incendo/hyperverse/platform/v1_18_2/NMSImpl.java b/hyperverse-nms-1-18/src/main/java/org/incendo/hyperverse/platform/v1_18_2/NMSImpl.java index 9dc79d97..81271245 100644 --- a/hyperverse-nms-1-18/src/main/java/org/incendo/hyperverse/platform/v1_18_2/NMSImpl.java +++ b/hyperverse-nms-1-18/src/main/java/org/incendo/hyperverse/platform/v1_18_2/NMSImpl.java @@ -19,7 +19,6 @@ import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -150,7 +149,7 @@ public class NMSImpl implements NMS { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); diff --git a/hyperverse-nms-1-19/src/main/java/org/incendo/hyperverse/platform/v1_19_4/NMSImpl.java b/hyperverse-nms-1-19/src/main/java/org/incendo/hyperverse/platform/v1_19_4/NMSImpl.java index 182997f8..f076fb47 100644 --- a/hyperverse-nms-1-19/src/main/java/org/incendo/hyperverse/platform/v1_19_4/NMSImpl.java +++ b/hyperverse-nms-1-19/src/main/java/org/incendo/hyperverse/platform/v1_19_4/NMSImpl.java @@ -19,7 +19,6 @@ import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -151,7 +150,7 @@ public class NMSImpl implements NMS { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); diff --git a/hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java b/hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java index 2910f092..ea371c1a 100644 --- a/hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java +++ b/hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java @@ -19,7 +19,6 @@ import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -152,7 +151,7 @@ public class NMSImpl implements NMS { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); diff --git a/hyperverse-nms-1-20/src/main/java/org/incendo/hyperverse/platform/v1_20_4/NMSImpl.java b/hyperverse-nms-1-20/src/main/java/org/incendo/hyperverse/platform/v1_20_4/NMSImpl.java index 162ea27b..bbedc54c 100644 --- a/hyperverse-nms-1-20/src/main/java/org/incendo/hyperverse/platform/v1_20_4/NMSImpl.java +++ b/hyperverse-nms-1-20/src/main/java/org/incendo/hyperverse/platform/v1_20_4/NMSImpl.java @@ -19,7 +19,6 @@ import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -152,7 +151,7 @@ public class NMSImpl implements NMS { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); diff --git a/hyperverse-nms-1-17/build.gradle.kts b/hyperverse-nms-1-21-1/build.gradle.kts similarity index 51% rename from hyperverse-nms-1-17/build.gradle.kts rename to hyperverse-nms-1-21-1/build.gradle.kts index f13fa1d1..80cb781e 100644 --- a/hyperverse-nms-1-17/build.gradle.kts +++ b/hyperverse-nms-1-21-1/build.gradle.kts @@ -3,17 +3,19 @@ plugins { alias(libs.plugins.paperweight.userdev) } +indra { + javaVersions { + minimumToolchain(21) + target(21) + } +} + dependencies { - paperweight.paperDevBundle("1.17.1-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") compileOnly(projects.hyperverseNmsCommon) } -java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) - tasks { - reobfJar { - outputJar.set(file("build/libs/${project.name}-${project.version}.jar")) - } assemble { dependsOn(reobfJar) } diff --git a/hyperverse-nms-1-17/src/main/java/org/incendo/hyperverse/platform/v1_17_1/NMSImpl.java b/hyperverse-nms-1-21-1/src/main/java/org/incendo/hyperverse/platform/v1_21_1/NMSImpl.java similarity index 73% rename from hyperverse-nms-1-17/src/main/java/org/incendo/hyperverse/platform/v1_17_1/NMSImpl.java rename to hyperverse-nms-1-21-1/src/main/java/org/incendo/hyperverse/platform/v1_21_1/NMSImpl.java index 6518bb1a..f7d737d7 100644 --- a/hyperverse-nms-1-17/src/main/java/org/incendo/hyperverse/platform/v1_17_1/NMSImpl.java +++ b/hyperverse-nms-1-21-1/src/main/java/org/incendo/hyperverse/platform/v1_21_1/NMSImpl.java @@ -15,57 +15,60 @@ // along with this program. If not, see . // -package org.incendo.hyperverse.platform.v1_17_1; +package org.incendo.hyperverse.platform.v1_21_1; import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; + import net.minecraft.BlockUtil; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.DoubleTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.entity.EntityLookup; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.portal.PortalForcer; +import net.minecraft.world.phys.Vec3; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.filter.RegexFilter; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerRespawnEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.incendo.hyperverse.util.HyperConfigShouldGroupProfiles; import org.incendo.hyperverse.util.NMS; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Objects; -import java.util.Optional; - @SuppressWarnings("unused") public class NMSImpl implements NMS { - private final TaskChainFactory taskChainFactory; + private final TaskChainFactory taskFactory; private Field entitySectionManager; private Field entityLookup; private org.apache.logging.log4j.core.Logger worldServerLogger; - @Inject public NMSImpl(final TaskChainFactory taskChainFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) { - this.taskChainFactory = taskChainFactory; + @Inject public NMSImpl(final TaskChainFactory taskFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) { + this.taskFactory = taskFactory; if (hyperConfiguration) { try { final Field field = ServerLevel.class.getDeclaredField("LOGGER"); @@ -76,8 +79,8 @@ public class NMSImpl implements NMS { } try { final RegexFilter regexFilter = RegexFilter - .createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false, - Filter.Result.DENY, Filter.Result.ACCEPT); + .createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false, + Filter.Result.DENY, Filter.Result.ACCEPT); this.worldServerLogger.addFilter(regexFilter); } catch (IllegalAccessException e) { e.printStackTrace(); @@ -86,27 +89,32 @@ public class NMSImpl implements NMS { } @Override @Nullable public Location getOrCreateNetherPortal(@NotNull final org.bukkit.entity.Entity entity, - @NotNull final Location origin) { + @NotNull final Location origin) { final ServerLevel worldServer = Objects.requireNonNull(((CraftWorld) origin.getWorld()).getHandle()); final PortalForcer portalTravelAgent = Objects.requireNonNull(worldServer.getPortalForcer()); final Entity nmsEntity = Objects.requireNonNull(((CraftEntity) entity).getHandle()); final BlockPos blockPosition = new BlockPos(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ()); - Optional portalShape = Objects.requireNonNull(portalTravelAgent, "travel agent") - .findPortal(Objects.requireNonNull(blockPosition, "position"), 128); - if (portalShape.isEmpty()) { - portalShape = portalTravelAgent.createPortal(blockPosition, nmsEntity.getDirection().getAxis(), nmsEntity, 16); + final WorldBorder worldBorder = worldServer.getWorldBorder(); + Optional existingPortalPosition = Objects.requireNonNull(portalTravelAgent, "travel agent") + .findClosestPortalPosition(Objects.requireNonNull(blockPosition, "position"), worldBorder,128); + if (existingPortalPosition.isPresent()) { + BlockPos bottomLeft = existingPortalPosition.get(); + return new Location(origin.getWorld(), bottomLeft.getX(), bottomLeft.getY(), bottomLeft.getZ()); } - if (portalShape.isEmpty()) { + Optional createdPortal = portalTravelAgent.createPortal(blockPosition, + nmsEntity.getDirection().getAxis(), nmsEntity, + 16); + if (createdPortal.isEmpty()) { return null; } - final BlockUtil.FoundRectangle rectangle = portalShape.get(); - return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1, rectangle.minCorner.getY() - 1, - rectangle.minCorner.getZ() + 1); + final BlockUtil.FoundRectangle rectangle = createdPortal.get(); + return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1D, rectangle.minCorner.getY() - 1D, + rectangle.minCorner.getZ() + 1D); } @Override @Nullable public Location getDimensionSpawn(@NotNull final Location origin) { if (Objects.requireNonNull(origin.getWorld()).getEnvironment() - == World.Environment.THE_END) { + == World.Environment.THE_END) { return new Location(origin.getWorld(), 100, 50, 0); } return origin.getWorld().getSpawnLocation(); @@ -114,7 +122,7 @@ public class NMSImpl implements NMS { @Override public void writePlayerData(@NotNull final Player player, @NotNull final Path file) { final CompoundTag playerTag = new CompoundTag(); - final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final net.minecraft.world.entity.player.Player entityPlayer = ((CraftPlayer) player).getHandle(); entityPlayer.save(playerTag); if (!playerTag.contains("hyperverse")) { @@ -124,7 +132,7 @@ public class NMSImpl implements NMS { hyperverse.putLong("writeTime", System.currentTimeMillis()); hyperverse.putString("version", Bukkit.getPluginManager().getPlugin("Hyperverse").getDescription().getVersion()); - this.taskChainFactory.newChain().async(() -> { + taskFactory.newChain().async(() -> { try (final OutputStream outputStream = Files.newOutputStream(file)) { NbtIo.writeCompressed(playerTag, outputStream); } catch (final Exception e) { @@ -135,19 +143,19 @@ public class NMSImpl implements NMS { @Override public void readPlayerData(@NotNull final Player player, @NotNull final Path file, @NotNull final Runnable whenDone) { final Location originLocation = player.getLocation().clone(); - this.taskChainFactory.newChain().asyncFirst(() -> { + taskFactory.newChain().asyncFirst(() -> { try (final InputStream inputStream = Files.newInputStream(file)) { - return Optional.of(NbtIo.readCompressed(inputStream)); + return Optional.of(NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap())); } catch (final Exception e) { e.printStackTrace(); } return Optional.empty(); }).syncLast((optionalCompound) -> { - if (!optionalCompound.isPresent()) { + if (optionalCompound.isEmpty()) { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); @@ -159,7 +167,7 @@ public class NMSImpl implements NMS { final int spawnY = compound.getInt("SpawnY"); final int spawnZ = compound.getInt("SpawnZ"); final Location spawnLocation = new Location(Bukkit.getWorld(spawnWorld), spawnX, - spawnY, spawnZ); + spawnY, spawnZ); final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle(); @@ -167,7 +175,8 @@ public class NMSImpl implements NMS { // mess up the profile ((CraftPlayer) player).setExtraData(compound); // Set the position to the player's current position - compound.put("Pos", doubleList(entityPlayer.getX(), entityPlayer.getY(), entityPlayer.getZ())); + Vec3 pos = entityPlayer.position(); + compound.put("Pos", doubleList(pos.x, pos.y, pos.z)); // Set the world to the player's current world compound.putString("world", player.getWorld().getName()); // Store persistent values @@ -183,10 +192,11 @@ public class NMSImpl implements NMS { final ServerLevel worldServer = ((CraftWorld) originLocation.getWorld()).getHandle(); final DimensionType dimensionManager = worldServer.dimensionType(); + // Prevent annoying message + // Spigot-obf = decouple() entityPlayer.unRide(); - // Removal reason == "CHANGED_DIMENSION" - worldServer.removePlayerImmediately(entityPlayer, net.minecraft.world.entity.Entity.RemovalReason.CHANGED_DIMENSION); + worldServer.removePlayerImmediately(entityPlayer, Entity.RemovalReason.CHANGED_DIMENSION); // worldServer.removePlayer above should remove the player from the // map, but that doesn't always happen. This is a last effort // attempt to prevent the annoying "Force re-added" message @@ -205,8 +215,11 @@ public class NMSImpl implements NMS { } catch (final NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } - entityPlayer.server.getPlayerList().moveToWorld(entityPlayer, worldServer, - true, originLocation, true); + + // pre 1.18 code = PlayerList#moveToWorld + entityPlayer.server.getPlayerList().remove(entityPlayer); + worldServer.getServer().getPlayerList().respawn(entityPlayer, true, + Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN, originLocation); // Apply health and foodLevel player.setHealth(health); @@ -222,18 +235,20 @@ public class NMSImpl implements NMS { if (craftWorld == null) { return null; } - return net.minecraft.world.entity.player.Player.findRespawnPositionAndUseSpawnBlock(craftWorld.getHandle(), new BlockPos(spawnLocation.getBlockX(), - spawnLocation.getBlockY(), spawnLocation.getBlockZ()), 0, true, false) - .map(vec3D -> new Location(spawnLocation.getWorld(), vec3D.x(), vec3D.y(), vec3D.z())) - .orElse(null); + + return ServerPlayer.findRespawnAndUseSpawnBlock(craftWorld.getHandle(), new BlockPos(spawnLocation.getBlockX(), + spawnLocation.getBlockY(), spawnLocation.getBlockZ()), 0, true, false) + .map(ServerPlayer.RespawnPosAngle::position) + .map(pos -> new Location(spawnLocation.getWorld(), pos.x(), pos.y(), pos.z())) + .orElse(null); } private static ListTag doubleList(final double... values) { - final ListTag nbttaglist = new ListTag(); + final ListTag tagList = new ListTag(); for (final double d : values) { - nbttaglist.add(DoubleTag.valueOf(d)); + tagList.add(DoubleTag.valueOf(d)); } - return nbttaglist; + return tagList; } } diff --git a/hyperverse-nms-1-21-3/build.gradle.kts b/hyperverse-nms-1-21-3/build.gradle.kts new file mode 100644 index 00000000..19a0053e --- /dev/null +++ b/hyperverse-nms-1-21-3/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("hyperverse.base-conventions") + alias(libs.plugins.paperweight.userdev) +} + +indra { + javaVersions { + minimumToolchain(21) + target(21) + } +} + +dependencies { + paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT") + compileOnly(projects.hyperverseNmsCommon) +} + +tasks { + assemble { + dependsOn(reobfJar) + } +} diff --git a/hyperverse-nms-1-21-3/src/main/java/org/incendo/hyperverse/platform/v1_21_3/NMSImpl.java b/hyperverse-nms-1-21-3/src/main/java/org/incendo/hyperverse/platform/v1_21_3/NMSImpl.java new file mode 100644 index 00000000..69aa0e4f --- /dev/null +++ b/hyperverse-nms-1-21-3/src/main/java/org/incendo/hyperverse/platform/v1_21_3/NMSImpl.java @@ -0,0 +1,254 @@ +// +// Hyperverse - A minecraft world management plugin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package org.incendo.hyperverse.platform.v1_21_3; + +import co.aikar.taskchain.TaskChainFactory; +import com.google.inject.Inject; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; + +import net.minecraft.BlockUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityLookup; +import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import net.minecraft.world.level.portal.PortalForcer; +import net.minecraft.world.phys.Vec3; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.filter.RegexFilter; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.incendo.hyperverse.util.HyperConfigShouldGroupProfiles; +import org.incendo.hyperverse.util.NMS; + +@SuppressWarnings("unused") +public class NMSImpl implements NMS { + + private final TaskChainFactory taskFactory; + private Field entitySectionManager; + private Field entityLookup; + private org.apache.logging.log4j.core.Logger worldServerLogger; + + @Inject public NMSImpl(final TaskChainFactory taskFactory, final @HyperConfigShouldGroupProfiles boolean hyperConfiguration) { + this.taskFactory = taskFactory; + if (hyperConfiguration) { + try { + final Field field = ServerLevel.class.getDeclaredField("LOGGER"); + field.setAccessible(true); + this.worldServerLogger = (Logger) field.get(null); + } catch (final Exception e) { + e.printStackTrace(); + } + try { + final RegexFilter regexFilter = RegexFilter + .createFilter("[\\S\\s]*Force-added player with duplicate UUID[\\S\\s]*", null, false, + Filter.Result.DENY, Filter.Result.ACCEPT); + this.worldServerLogger.addFilter(regexFilter); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + @Override @Nullable public Location getOrCreateNetherPortal(@NotNull final org.bukkit.entity.Entity entity, + @NotNull final Location origin) { + final ServerLevel worldServer = Objects.requireNonNull(((CraftWorld) origin.getWorld()).getHandle()); + final PortalForcer portalTravelAgent = Objects.requireNonNull(worldServer.getPortalForcer()); + final Entity nmsEntity = Objects.requireNonNull(((CraftEntity) entity).getHandle()); + final BlockPos blockPosition = new BlockPos(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ()); + final WorldBorder worldBorder = worldServer.getWorldBorder(); + Optional existingPortalPosition = Objects.requireNonNull(portalTravelAgent, "travel agent") + .findClosestPortalPosition(Objects.requireNonNull(blockPosition, "position"), worldBorder,128); + if (existingPortalPosition.isPresent()) { + BlockPos bottomLeft = existingPortalPosition.get(); + return new Location(origin.getWorld(), bottomLeft.getX(), bottomLeft.getY(), bottomLeft.getZ()); + } + Optional createdPortal = portalTravelAgent.createPortal(blockPosition, + nmsEntity.getDirection().getAxis(), nmsEntity, + 16); + if (createdPortal.isEmpty()) { + return null; + } + final BlockUtil.FoundRectangle rectangle = createdPortal.get(); + return new Location(origin.getWorld(), rectangle.minCorner.getX() + 1D, rectangle.minCorner.getY() - 1D, + rectangle.minCorner.getZ() + 1D); + } + + @Override @Nullable public Location getDimensionSpawn(@NotNull final Location origin) { + if (Objects.requireNonNull(origin.getWorld()).getEnvironment() + == World.Environment.THE_END) { + return new Location(origin.getWorld(), 100, 50, 0); + } + return origin.getWorld().getSpawnLocation(); + } + + @Override public void writePlayerData(@NotNull final Player player, @NotNull final Path file) { + final CompoundTag playerTag = new CompoundTag(); + final net.minecraft.world.entity.player.Player entityPlayer = ((CraftPlayer) player).getHandle(); + entityPlayer.save(playerTag); + + if (!playerTag.contains("hyperverse")) { + playerTag.put("hyperverse", new CompoundTag()); + } + final CompoundTag hyperverse = playerTag.getCompound("hyperverse"); + hyperverse.putLong("writeTime", System.currentTimeMillis()); + hyperverse.putString("version", Bukkit.getPluginManager().getPlugin("Hyperverse").getDescription().getVersion()); + + taskFactory.newChain().async(() -> { + try (final OutputStream outputStream = Files.newOutputStream(file)) { + NbtIo.writeCompressed(playerTag, outputStream); + } catch (final Exception e) { + e.printStackTrace(); + } + }).execute(); + } + + @Override public void readPlayerData(@NotNull final Player player, @NotNull final Path file, @NotNull final Runnable whenDone) { + final Location originLocation = player.getLocation().clone(); + taskFactory.newChain().asyncFirst(() -> { + try (final InputStream inputStream = Files.newInputStream(file)) { + return Optional.of(NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap())); + } catch (final Exception e) { + e.printStackTrace(); + } + return Optional.empty(); + }).syncLast((optionalCompound) -> { + if (optionalCompound.isEmpty()) { + return; + } + final CompoundTag compound = (CompoundTag) optionalCompound.get(); + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { + // Health and hunger don't update properly, so we + // give them a little help + final float health = compound.getFloat("Health"); + final int foodLevel = compound.getInt("foodLevel"); + + // Restore bed spawn + final String spawnWorld = compound.getString("SpawnWorld"); + final int spawnX = compound.getInt("SpawnX"); + final int spawnY = compound.getInt("SpawnY"); + final int spawnZ = compound.getInt("SpawnZ"); + final Location spawnLocation = new Location(Bukkit.getWorld(spawnWorld), spawnX, + spawnY, spawnZ); + + final ServerPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + + // We re-write the extra Bukkit data as to not + // mess up the profile + ((CraftPlayer) player).setExtraData(compound); + // Set the position to the player's current position + Vec3 pos = entityPlayer.position(); + compound.put("Pos", doubleList(pos.x, pos.y, pos.z)); + // Set the world to the player's current world + compound.putString("world", player.getWorld().getName()); + // Store persistent values + ((CraftPlayer) player).storeBukkitValues(compound); + + // We start by doing a total reset + entityPlayer.reset(); + entityPlayer.load(compound); + + entityPlayer.effectsDirty = true; + entityPlayer.onUpdateAbilities(); + player.teleport(originLocation); + + final ServerLevel worldServer = ((CraftWorld) originLocation.getWorld()).getHandle(); + final DimensionType dimensionManager = worldServer.dimensionType(); + + // Prevent annoying message + // Spigot-obf = decouple() + entityPlayer.unRide(); + worldServer.removePlayerImmediately(entityPlayer, Entity.RemovalReason.CHANGED_DIMENSION); + // worldServer.removePlayer above should remove the player from the + // map, but that doesn't always happen. This is a last effort + // attempt to prevent the annoying "Force re-added" message + // from appearing + try { + if (this.entitySectionManager == null) { + this.entitySectionManager = worldServer.getClass().getDeclaredField("entityManager"); + this.entitySectionManager.setAccessible(true); + } + final PersistentEntitySectionManager esm = (PersistentEntitySectionManager) this.entitySectionManager.get(worldServer); + if (this.entityLookup == null) { + this.entityLookup = esm.getClass().getDeclaredField("visibleEntityStorage"); + } + final EntityLookup lookup = (EntityLookup) this.entityLookup.get(esm); + lookup.remove(entityPlayer); + } catch (final NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + // pre 1.18 code = PlayerList#moveToWorld + entityPlayer.server.getPlayerList().remove(entityPlayer); + worldServer.getServer().getPlayerList().respawn(entityPlayer, true, + Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN, originLocation); + + // Apply health and foodLevel + player.setHealth(health); + player.setFoodLevel(foodLevel); + player.setPortalCooldown(40); + player.setBedSpawnLocation(spawnLocation, true); + }); + }).execute(whenDone); + } + + @Override @Nullable public Location findBedRespawn(@NotNull final Location spawnLocation) { + final CraftWorld craftWorld = (CraftWorld) spawnLocation.getWorld(); + if (craftWorld == null) { + return null; + } + + return ServerPlayer.findRespawnAndUseSpawnBlock(craftWorld.getHandle(), new BlockPos(spawnLocation.getBlockX(), + spawnLocation.getBlockY(), spawnLocation.getBlockZ()), 0, true, false) + .map(ServerPlayer.RespawnPosAngle::position) + .map(pos -> new Location(spawnLocation.getWorld(), pos.x(), pos.y(), pos.z())) + .orElse(null); + } + + private static ListTag doubleList(final double... values) { + final ListTag tagList = new ListTag(); + for (final double d : values) { + tagList.add(DoubleTag.valueOf(d)); + } + return tagList; + } + +} diff --git a/hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java b/hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java index ea5f6e46..d886ed51 100644 --- a/hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java +++ b/hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java @@ -19,7 +19,6 @@ import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; -import io.papermc.lib.PaperLib; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -156,7 +155,7 @@ public class NMSImpl implements NMS { return; } final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + originLocation.getWorld().getChunkAtAsync(originLocation).thenAccept(chunk -> { // Health and hunger don't update properly, so we // give them a little help final float health = compound.getFloat("Health"); diff --git a/hyperverse-nms-common/build.gradle.kts b/hyperverse-nms-common/build.gradle.kts index e39a3bd1..eed43a1b 100644 --- a/hyperverse-nms-common/build.gradle.kts +++ b/hyperverse-nms-common/build.gradle.kts @@ -4,7 +4,6 @@ plugins { dependencies { compileOnly(libs.paper) - compileOnlyApi(libs.paperlib) compileOnlyApi(libs.guice) compileOnlyApi(libs.taskchain) } diff --git a/settings.gradle.kts b/settings.gradle.kts index a90ecc1d..345c2d68 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,9 +13,10 @@ include(":hyperverse-nms-common") include(":hyperverse-core") include(":hyperverse-nms-unsupported") -include(":hyperverse-nms-1-17") include(":hyperverse-nms-1-18") include(":hyperverse-nms-1-19") include(":hyperverse-nms-1-20") include(":hyperverse-nms-1-20-6") include(":hyperverse-nms-1-21") +include(":hyperverse-nms-1-21-1") +include(":hyperverse-nms-1-21-3")