From c4dd5372cd9e916751bc364db687151a98bf5621 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 27 Jul 2025 21:10:33 -0500 Subject: [PATCH 1/5] Add support for 1.21.8 --- hyperverse-core/build.gradle.kts | 4 +- .../org/incendo/hyperverse/Hyperverse.java | 3 +- hyperverse-nms-1-21-8/build.gradle.kts | 22 ++ .../hyperverse/platform/v1_21_8/NMSImpl.java | 280 ++++++++++++++++++ settings.gradle.kts | 1 + 5 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 hyperverse-nms-1-21-8/build.gradle.kts create mode 100644 hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java diff --git a/hyperverse-core/build.gradle.kts b/hyperverse-core/build.gradle.kts index 3758e66..91728ac 100644 --- a/hyperverse-core/build.gradle.kts +++ b/hyperverse-core/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { runtimeOnly(projects.hyperverseNms1215) runtimeOnly(projects.hyperverseNms1216) runtimeOnly(projects.hyperverseNms1217) + runtimeOnly(projects.hyperverseNms1218) } bukkit { @@ -113,6 +114,7 @@ tasks { exclude(project(":hyperverse-nms-1-21-5")) exclude(project(":hyperverse-nms-1-21-6")) exclude(project(":hyperverse-nms-1-21-7")) + exclude(project(":hyperverse-nms-1-21-8")) } mergeServiceFiles() @@ -150,6 +152,6 @@ tasks { runServer { java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - minecraftVersion("1.21.7") + minecraftVersion("1.21.8") } } 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 2c1ea58..912b2bb 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java @@ -99,7 +99,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste Version.parseMinecraft("1.21.4"), Version.parseMinecraft("1.21.5"), Version.parseMinecraft("1.21.6"), - Version.parseMinecraft("1.21.7") + Version.parseMinecraft("1.21.7"), + Version.parseMinecraft("1.21.8") ); private WorldManager worldManager; diff --git a/hyperverse-nms-1-21-8/build.gradle.kts b/hyperverse-nms-1-21-8/build.gradle.kts new file mode 100644 index 0000000..680fbb8 --- /dev/null +++ b/hyperverse-nms-1-21-8/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.8-R0.1-SNAPSHOT") + compileOnly(projects.hyperverseNmsCommon) +} + +tasks { + assemble { + dependsOn(reobfJar) + } +} diff --git a/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java b/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java new file mode 100644 index 0000000..f98a309 --- /dev/null +++ b/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java @@ -0,0 +1,280 @@ +// +// 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_8; + +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.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +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.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +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(TagValueOutput.createWrappingGlobal(null, playerTag)); + + // Handle the Optional return type of getCompound + CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); + // If "hyperverse" didn't exist, we need to add it to playerTag + playerTag.put("hyperverse", 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.isPresent()) { + return; + } + final CompoundTag compound = (CompoundTag) optionalCompound.get(); + PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + // Health and hunger don't update properly, so we give them a little help + final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) + final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) + + // Restore bed spawn + final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world + final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); + final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); + final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); + 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); + + // We start by doing a total reset + entityPlayer.reset(); + entityPlayer.load(TagValueInput.createGlobal(null, 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 + 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.getServer().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; + } + + // Create a BlockPos for the spawn location + BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); + + // Get the dimension ResourceKey from the ServerLevel + ServerLevel serverLevel = craftWorld.getHandle(); + ResourceKey dimension = serverLevel.dimension(); + + // Create a RespawnConfig instance with the required arguments + ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( + dimension, // The dimension of the world + blockPos, // The position to check for respawn + 0.0f, // Yaw (rotation angle), default to 0 + true // Forced flag, set to true to match previous behavior + ); + + // Call the method with the correct arguments + return ServerPlayer.findRespawnAndUseSpawnBlock( + serverLevel, + respawnConfig, + false // Assuming the boolean is respawnAfterDeath + ) + .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/settings.gradle.kts b/settings.gradle.kts index 1b09a53..68d2d31 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,3 +20,4 @@ include(":hyperverse-nms-1-21-4") include(":hyperverse-nms-1-21-5") include(":hyperverse-nms-1-21-6") include(":hyperverse-nms-1-21-7") +include(":hyperverse-nms-1-21-8") From bb077db5fa21ed757a10f6f2e45c67250555d6ee Mon Sep 17 00:00:00 2001 From: Snewmy Date: Sun, 2 Nov 2025 19:58:09 -0600 Subject: [PATCH 2/5] Add 1.21.10 support --- gradle.properties | 2 +- hyperverse-core/build.gradle.kts | 4 +- .../org/incendo/hyperverse/Hyperverse.java | 3 +- hyperverse-nms-1-21-10/build.gradle.kts | 22 ++ .../hyperverse/platform/v1_21_10/NMSImpl.java | 286 ++++++++++++++++++ settings.gradle.kts | 1 + 6 files changed, 315 insertions(+), 3 deletions(-) create mode 100644 hyperverse-nms-1-21-10/build.gradle.kts create mode 100644 hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java diff --git a/gradle.properties b/gradle.properties index cdcfb35..f687e0c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,6 @@ version=1.0.0-SNAPSHOT description=Minecraft world management plugin org.gradle.parallel=true -org.gradle.jvmargs=-Xmx512m +org.gradle.jvmargs=-Xmx2G org.gradle.java.installations.auto-download=false org.gradle.caching=true diff --git a/hyperverse-core/build.gradle.kts b/hyperverse-core/build.gradle.kts index 3758e66..54ecf19 100644 --- a/hyperverse-core/build.gradle.kts +++ b/hyperverse-core/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { runtimeOnly(projects.hyperverseNms1215) runtimeOnly(projects.hyperverseNms1216) runtimeOnly(projects.hyperverseNms1217) + runtimeOnly(projects.hyperverseNms12110) } bukkit { @@ -113,6 +114,7 @@ tasks { exclude(project(":hyperverse-nms-1-21-5")) exclude(project(":hyperverse-nms-1-21-6")) exclude(project(":hyperverse-nms-1-21-7")) + exclude(project(":hyperverse-nms-1-21-10")) } mergeServiceFiles() @@ -150,6 +152,6 @@ tasks { runServer { java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - minecraftVersion("1.21.7") + minecraftVersion("1.21.10") } } 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 2c1ea58..47d1e27 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java @@ -99,7 +99,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste Version.parseMinecraft("1.21.4"), Version.parseMinecraft("1.21.5"), Version.parseMinecraft("1.21.6"), - Version.parseMinecraft("1.21.7") + Version.parseMinecraft("1.21.7"), + Version.parseMinecraft("1.21.10") ); private WorldManager worldManager; diff --git a/hyperverse-nms-1-21-10/build.gradle.kts b/hyperverse-nms-1-21-10/build.gradle.kts new file mode 100644 index 0000000..f97e9ac --- /dev/null +++ b/hyperverse-nms-1-21-10/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.10-R0.1-SNAPSHOT") + compileOnly(projects.hyperverseNmsCommon) +} + +tasks { + assemble { + dependsOn(reobfJar) + } +} diff --git a/hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java b/hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java new file mode 100644 index 0000000..a7b3909 --- /dev/null +++ b/hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java @@ -0,0 +1,286 @@ +// +// 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_10; + +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.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +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.level.storage.LevelData; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +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(TagValueOutput.createWrappingGlobal(null, playerTag)); + + // Handle the Optional return type of getCompound + CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); + // If "hyperverse" didn't exist, we need to add it to playerTag + playerTag.put("hyperverse", 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.isPresent()) { + return; + } + final CompoundTag compound = (CompoundTag) optionalCompound.get(); + PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + // Health and hunger don't update properly, so we give them a little help + final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) + final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) + + // Restore bed spawn + final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world + final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); + final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); + final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); + 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); + + // We start by doing a total reset + entityPlayer.reset(); + entityPlayer.load(TagValueInput.createGlobal(null, 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 + 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 + worldServer.getServer().getPlayerList().remove(entityPlayer); + worldServer.getServer().getPlayerList().respawn(entityPlayer, true, + Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN); + + // 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; + } + + // Create a BlockPos for the spawn location + BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); + + // Get the dimension ResourceKey from the ServerLevel + ServerLevel serverLevel = craftWorld.getHandle(); + ResourceKey dimension = serverLevel.dimension(); + + LevelData.RespawnData respawnData = LevelData.RespawnData.of( + dimension, + blockPos, + 0.0f, + 0 + ); + + // Create a RespawnConfig instance with the required arguments + ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( + respawnData, // Yaw (rotation angle), default to 0 + true // Forced flag, set to true to match previous behavior + ); + + // Call the method with the correct arguments + return ServerPlayer.findRespawnAndUseSpawnBlock( + serverLevel, + respawnConfig, + false // Assuming the boolean is respawnAfterDeath + ) + .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/settings.gradle.kts b/settings.gradle.kts index 1b09a53..0fbdeda 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,3 +20,4 @@ include(":hyperverse-nms-1-21-4") include(":hyperverse-nms-1-21-5") include(":hyperverse-nms-1-21-6") include(":hyperverse-nms-1-21-7") +include(":hyperverse-nms-1-21-10") From e294eba3666031ec7a17ce6c406f8106a01ff7cb Mon Sep 17 00:00:00 2001 From: Snewmy Date: Wed, 7 Jan 2026 16:04:20 -0600 Subject: [PATCH 3/5] Add 1.21.11 Support --- hyperverse-core/build.gradle.kts | 4 +- .../org/incendo/hyperverse/Hyperverse.java | 3 +- hyperverse-nms-1-21-11/build.gradle.kts | 22 ++ .../hyperverse/platform/v1_21_11/NMSImpl.java | 286 ++++++++++++++++++ settings.gradle.kts | 1 + 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 hyperverse-nms-1-21-11/build.gradle.kts create mode 100644 hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java diff --git a/hyperverse-core/build.gradle.kts b/hyperverse-core/build.gradle.kts index ec73a1c..6c08747 100644 --- a/hyperverse-core/build.gradle.kts +++ b/hyperverse-core/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { runtimeOnly(projects.hyperverseNms1217) runtimeOnly(projects.hyperverseNms1218) runtimeOnly(projects.hyperverseNms12110) + runtimeOnly(projects.hyperverseNms12111) } bukkit { @@ -117,6 +118,7 @@ tasks { exclude(project(":hyperverse-nms-1-21-7")) exclude(project(":hyperverse-nms-1-21-8")) exclude(project(":hyperverse-nms-1-21-10")) + exclude(project(":hyperverse-nms-1-21-11")) } mergeServiceFiles() @@ -154,6 +156,6 @@ tasks { runServer { java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - minecraftVersion("1.21.10") + minecraftVersion("1.21.11") } } 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 8f3ec23..750b292 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java @@ -101,7 +101,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste Version.parseMinecraft("1.21.6"), Version.parseMinecraft("1.21.7"), Version.parseMinecraft("1.21.8"), - Version.parseMinecraft("1.21.10") + Version.parseMinecraft("1.21.10"), + Version.parseMinecraft("1.21.11") ); private WorldManager worldManager; diff --git a/hyperverse-nms-1-21-11/build.gradle.kts b/hyperverse-nms-1-21-11/build.gradle.kts new file mode 100644 index 0000000..6359df3 --- /dev/null +++ b/hyperverse-nms-1-21-11/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.11-R0.1-SNAPSHOT") + compileOnly(projects.hyperverseNmsCommon) +} + +tasks { + assemble { + dependsOn(reobfJar) + } +} diff --git a/hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java b/hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java new file mode 100644 index 0000000..630020f --- /dev/null +++ b/hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java @@ -0,0 +1,286 @@ +// +// 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_11; + +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.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.BlockUtil; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +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.level.storage.LevelData; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +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(TagValueOutput.createWrappingGlobal(null, playerTag)); + + // Handle the Optional return type of getCompound + CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); + // If "hyperverse" didn't exist, we need to add it to playerTag + playerTag.put("hyperverse", 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.isPresent()) { + return; + } + final CompoundTag compound = (CompoundTag) optionalCompound.get(); + PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { + // Health and hunger don't update properly, so we give them a little help + final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) + final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) + + // Restore bed spawn + final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world + final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); + final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); + final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); + 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); + + // We start by doing a total reset + entityPlayer.reset(); + entityPlayer.load(TagValueInput.createGlobal(null, 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 + 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 + worldServer.getServer().getPlayerList().remove(entityPlayer); + worldServer.getServer().getPlayerList().respawn(entityPlayer, true, + Entity.RemovalReason.CHANGED_DIMENSION, PlayerRespawnEvent.RespawnReason.PLUGIN); + + // 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; + } + + // Create a BlockPos for the spawn location + BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); + + // Get the dimension ResourceKey from the ServerLevel + ServerLevel serverLevel = craftWorld.getHandle(); + ResourceKey dimension = serverLevel.dimension(); + + LevelData.RespawnData respawnData = LevelData.RespawnData.of( + dimension, + blockPos, + 0.0f, + 0 + ); + + // Create a RespawnConfig instance with the required arguments + ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( + respawnData, // Yaw (rotation angle), default to 0 + true // Forced flag, set to true to match previous behavior + ); + + // Call the method with the correct arguments + return ServerPlayer.findRespawnAndUseSpawnBlock( + serverLevel, + respawnConfig, + false // Assuming the boolean is respawnAfterDeath + ) + .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/settings.gradle.kts b/settings.gradle.kts index 95f751e..411ddbf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,3 +22,4 @@ include(":hyperverse-nms-1-21-6") include(":hyperverse-nms-1-21-7") include(":hyperverse-nms-1-21-8") include(":hyperverse-nms-1-21-10") +include(":hyperverse-nms-1-21-11") From 01cec21cb00b9414629b3065f0741f9998fc72a3 Mon Sep 17 00:00:00 2001 From: Snewmy Date: Thu, 16 Jul 2026 16:59:08 -0500 Subject: [PATCH 4/5] Add 26.2 support --- gradle/libs.versions.toml | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- hyperverse-core/build.gradle.kts | 32 +- .../org/incendo/hyperverse/Hyperverse.java | 12 +- .../platform/ReflectionPlatformProvider.java | 16 +- .../hyperverse/util/versioning/Version.java | 29 +- .../hyperverse/platform/v1_20_6/NMSImpl.java | 248 ---------------- hyperverse-nms-1-21-10/build.gradle.kts | 22 -- hyperverse-nms-1-21-11/build.gradle.kts | 22 -- hyperverse-nms-1-21-3/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_3/NMSImpl.java | 255 ---------------- hyperverse-nms-1-21-4/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_4/NMSImpl.java | 255 ---------------- hyperverse-nms-1-21-5/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_5/NMSImpl.java | 278 ----------------- hyperverse-nms-1-21-6/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_6/NMSImpl.java | 280 ------------------ hyperverse-nms-1-21-7/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_7/NMSImpl.java | 280 ------------------ hyperverse-nms-1-21-8/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21_8/NMSImpl.java | 280 ------------------ hyperverse-nms-1-21/build.gradle.kts | 22 -- .../hyperverse/platform/v1_21/NMSImpl.java | 255 ---------------- .../build.gradle.kts | 12 +- .../hyperverse/platform/v26_1_2}/NMSImpl.java | 5 +- .../NMSImpl.java | 7 +- settings.gradle.kts | 13 +- 27 files changed, 55 insertions(+), 2408 deletions(-) delete mode 100644 hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-10/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-11/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-3/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-3/src/main/java/org/incendo/hyperverse/platform/v1_21_3/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-4/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-4/src/main/java/org/incendo/hyperverse/platform/v1_21_4/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-5/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-5/src/main/java/org/incendo/hyperverse/platform/v1_21_5/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-6/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-6/src/main/java/org/incendo/hyperverse/platform/v1_21_6/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-7/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-7/src/main/java/org/incendo/hyperverse/platform/v1_21_7/NMSImpl.java delete mode 100644 hyperverse-nms-1-21-8/build.gradle.kts delete mode 100644 hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java delete mode 100644 hyperverse-nms-1-21/build.gradle.kts delete mode 100644 hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java rename {hyperverse-nms-1-20-6 => hyperverse-nms-26-1}/build.gradle.kts (54%) rename {hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11 => hyperverse-nms-26-1/src/main/java/org/incendo/hyperverse/platform/v26_1_2}/NMSImpl.java (98%) rename {hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10 => hyperverse-nms-26-2/src/main/java/org.incendo.hyperverse.platform.v26_2}/NMSImpl.java (98%) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3905cdd..d07d0fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,9 +3,9 @@ cloud-build-logic = "0.0.3" ktlint = "0.50.0" checkstyle = "10.12.5" kotlin = "1.9.20" -shadow = "9.0.0-beta8" -paperweight = "2.0.0-beta.17" -run-paper = "2.3.1" +shadow = "9.4.1" +paperweight = "2.0.0-beta.21" +run-paper = "3.0.2" pluginyml = "0.6.0" # Platforms diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867..c61a118 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/hyperverse-core/build.gradle.kts b/hyperverse-core/build.gradle.kts index 6c08747..2d6a991 100644 --- a/hyperverse-core/build.gradle.kts +++ b/hyperverse-core/build.gradle.kts @@ -44,16 +44,8 @@ dependencies { implementation("net.kyori:adventure-text-minimessage:4.14.0") implementation(projects.hyperverseNmsUnsupported) - runtimeOnly(projects.hyperverseNms1206) - runtimeOnly(projects.hyperverseNms121) - runtimeOnly(projects.hyperverseNms1213) - runtimeOnly(projects.hyperverseNms1214) - runtimeOnly(projects.hyperverseNms1215) - runtimeOnly(projects.hyperverseNms1216) - runtimeOnly(projects.hyperverseNms1217) - runtimeOnly(projects.hyperverseNms1218) - runtimeOnly(projects.hyperverseNms12110) - runtimeOnly(projects.hyperverseNms12111) + runtimeOnly(projects.hyperverseNms261) + runtimeOnly(projects.hyperverseNms262) } bukkit { @@ -98,7 +90,9 @@ bukkit { } } } - +java { + disableAutoTargetJvm() +} tasks { processResources { filesMatching("plugin.yml") { @@ -109,16 +103,8 @@ tasks { shadowJar { minimize { exclude(project(":hyperverse-nms-unsupported")) - exclude(project(":hyperverse-nms-1-20-6")) - exclude(project(":hyperverse-nms-1-21")) - exclude(project(":hyperverse-nms-1-21-3")) - exclude(project(":hyperverse-nms-1-21-4")) - exclude(project(":hyperverse-nms-1-21-5")) - exclude(project(":hyperverse-nms-1-21-6")) - exclude(project(":hyperverse-nms-1-21-7")) - exclude(project(":hyperverse-nms-1-21-8")) - exclude(project(":hyperverse-nms-1-21-10")) - exclude(project(":hyperverse-nms-1-21-11")) + exclude(project(":hyperverse-nms-26-1")) + exclude(project(":hyperverse-nms-26-2")) } mergeServiceFiles() @@ -155,7 +141,7 @@ tasks { } runServer { - java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - minecraftVersion("1.21.11") + java.toolchain.languageVersion.set(JavaLanguageVersion.of(25)) + minecraftVersion("26.2") } } 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 750b292..e0be6e7 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/Hyperverse.java @@ -93,16 +93,8 @@ public final class Hyperverse extends JavaPlugin implements HyperverseAPI, Liste private final ServicePipeline servicePipeline = ServicePipeline.builder().build(); private final List supportedVersions = List.of( - Version.parseMinecraft("1.20.6"), - Version.parseMinecraft("1.21.1"), - Version.parseMinecraft("1.21.3"), - Version.parseMinecraft("1.21.4"), - Version.parseMinecraft("1.21.5"), - Version.parseMinecraft("1.21.6"), - Version.parseMinecraft("1.21.7"), - Version.parseMinecraft("1.21.8"), - Version.parseMinecraft("1.21.10"), - Version.parseMinecraft("1.21.11") + Version.parseMinecraft("26.1.2"), + Version.parseMinecraft("26.2") ); private WorldManager worldManager; diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/platform/ReflectionPlatformProvider.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/platform/ReflectionPlatformProvider.java index 304af39..9ef79dd 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/platform/ReflectionPlatformProvider.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/platform/ReflectionPlatformProvider.java @@ -32,13 +32,25 @@ public ReflectionPlatformProvider(@NotNull final Version version) { @Override public @NotNull Class providePlatform() throws PlatformProvisionException { - String expectedPackage = this.minecraftVersion.original().toLowerCase(Locale.ENGLISH).replace('.', '_'); + var data = this.minecraftVersion.versionData(); + + String expectedPackage; + if (data.patch() == 0) { + expectedPackage = data.major() + "_" + data.minor(); + } else { + expectedPackage = data.major() + "_" + data.minor() + "_" + data.patch(); + } + String packageName = "org.incendo.hyperverse.platform.v" + expectedPackage; + try { Class clazz = Class.forName(packageName + ".NMSImpl"); return clazz.asSubclass(NMS.class); } catch (ReflectiveOperationException ex) { - throw new PlatformProvisionException("Could not provide platform for version: " + this.minecraftVersion + "!", ex); + throw new PlatformProvisionException( + "Could not provide platform for version: " + this.minecraftVersion + "!", + ex + ); } } diff --git a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/versioning/Version.java b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/versioning/Version.java index ba07b79..9359f53 100644 --- a/hyperverse-core/src/main/java/org/incendo/hyperverse/util/versioning/Version.java +++ b/hyperverse-core/src/main/java/org/incendo/hyperverse/util/versioning/Version.java @@ -32,21 +32,26 @@ public record Version(@NotNull String original, @NotNull VersionData versionData public static final Pattern SEMVER_PATTERN = Pattern.compile( "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"); - public static @NotNull Version parseMinecraft(@NotNull final String version) throws IllegalArgumentException { - String[] split = version.split("\\."); - if (split.length < 2) { + public static @NotNull Version parseMinecraft(@NotNull final String version) + throws IllegalArgumentException { + + // Extract only x.y or x.y.z from beginning + Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.(\\d+))?") + .matcher(version); + + if (!matcher.find()) { throw new IllegalArgumentException("Invalid minecraft version: " + version); } - if (split.length == 2) { - StringJoiner joiner = new StringJoiner("."); - // insert a .0 to make it correctly formatted - joiner.add(split[0]); - joiner.add("0"); - joiner.add(split[1]); - Version formatted = parseSemVer(joiner.toString()); - return new Version(version, formatted.versionData()); + + String major = matcher.group(1); + String minor = matcher.group(2); + String patch = matcher.group(3); + + if (patch == null) { + patch = "0"; } - return parseSemVer(version); + + return parseSemVer(major + "." + minor + "." + patch); } public static @NotNull Version parseSemVer(@NotNull final String version) throws IllegalArgumentException { 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 deleted file mode 100644 index 2910f09..0000000 --- a/hyperverse-nms-1-20-6/src/main/java/org/incendo/hyperverse/platform/v1_20_6/NMSImpl.java +++ /dev/null @@ -1,248 +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.platform.v1_20_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; -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 portalShape = Objects.requireNonNull(portalTravelAgent, "travel agent") - .findPortalAround(Objects.requireNonNull(blockPosition, "position"), worldBorder,128); - if (portalShape.isEmpty()) { - portalShape = portalTravelAgent.createPortal(blockPosition, nmsEntity.getDirection().getAxis(), nmsEntity, 16); - } - if (portalShape.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); - } - - @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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.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, worldServer, true, originLocation, true, PlayerRespawnEvent.RespawnReason.PLUGIN); - - // 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 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); - } - - 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-10/build.gradle.kts b/hyperverse-nms-1-21-10/build.gradle.kts deleted file mode 100644 index f97e9ac..0000000 --- a/hyperverse-nms-1-21-10/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-11/build.gradle.kts b/hyperverse-nms-1-21-11/build.gradle.kts deleted file mode 100644 index 6359df3..0000000 --- a/hyperverse-nms-1-21-11/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-3/build.gradle.kts b/hyperverse-nms-1-21-3/build.gradle.kts deleted file mode 100644 index 19a0053..0000000 --- a/hyperverse-nms-1-21-3/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 792be08..0000000 --- a/hyperverse-nms-1-21-3/src/main/java/org/incendo/hyperverse/platform/v1_21_3/NMSImpl.java +++ /dev/null @@ -1,255 +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.platform.v1_21_3; - -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.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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.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-4/build.gradle.kts b/hyperverse-nms-1-21-4/build.gradle.kts deleted file mode 100644 index 5e67c30..0000000 --- a/hyperverse-nms-1-21-4/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-4/src/main/java/org/incendo/hyperverse/platform/v1_21_4/NMSImpl.java b/hyperverse-nms-1-21-4/src/main/java/org/incendo/hyperverse/platform/v1_21_4/NMSImpl.java deleted file mode 100644 index 9eccd95..0000000 --- a/hyperverse-nms-1-21-4/src/main/java/org/incendo/hyperverse/platform/v1_21_4/NMSImpl.java +++ /dev/null @@ -1,255 +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.platform.v1_21_4; - -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.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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.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-5/build.gradle.kts b/hyperverse-nms-1-21-5/build.gradle.kts deleted file mode 100644 index 1eb7e8b..0000000 --- a/hyperverse-nms-1-21-5/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-5/src/main/java/org/incendo/hyperverse/platform/v1_21_5/NMSImpl.java b/hyperverse-nms-1-21-5/src/main/java/org/incendo/hyperverse/platform/v1_21_5/NMSImpl.java deleted file mode 100644 index 032010b..0000000 --- a/hyperverse-nms-1-21-5/src/main/java/org/incendo/hyperverse/platform/v1_21_5/NMSImpl.java +++ /dev/null @@ -1,278 +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.platform.v1_21_5; - -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.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -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); - - // Handle the Optional return type of getCompound - CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); - // If "hyperverse" didn't exist, we need to add it to playerTag - playerTag.put("hyperverse", 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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { - // Health and hunger don't update properly, so we give them a little help - final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) - final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) - - // Restore bed spawn - final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world - final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); - final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); - final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); - 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 - 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; - } - - // Create a BlockPos for the spawn location - BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); - - // Get the dimension ResourceKey from the ServerLevel - ServerLevel serverLevel = craftWorld.getHandle(); - ResourceKey dimension = serverLevel.dimension(); - - // Create a RespawnConfig instance with the required arguments - ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( - dimension, // The dimension of the world - blockPos, // The position to check for respawn - 0.0f, // Yaw (rotation angle), default to 0 - true // Forced flag, set to true to match previous behavior - ); - - // Call the method with the correct arguments - return ServerPlayer.findRespawnAndUseSpawnBlock( - serverLevel, - respawnConfig, - false // Assuming the boolean is respawnAfterDeath - ) - .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-6/build.gradle.kts b/hyperverse-nms-1-21-6/build.gradle.kts deleted file mode 100644 index 4e0c55c..0000000 --- a/hyperverse-nms-1-21-6/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.6-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-6/src/main/java/org/incendo/hyperverse/platform/v1_21_6/NMSImpl.java b/hyperverse-nms-1-21-6/src/main/java/org/incendo/hyperverse/platform/v1_21_6/NMSImpl.java deleted file mode 100644 index 88a0513..0000000 --- a/hyperverse-nms-1-21-6/src/main/java/org/incendo/hyperverse/platform/v1_21_6/NMSImpl.java +++ /dev/null @@ -1,280 +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.platform.v1_21_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; -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.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -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.level.storage.TagValueInput; -import net.minecraft.world.level.storage.TagValueOutput; -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(TagValueOutput.createWrappingGlobal(null, playerTag)); - - // Handle the Optional return type of getCompound - CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); - // If "hyperverse" didn't exist, we need to add it to playerTag - playerTag.put("hyperverse", 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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { - // Health and hunger don't update properly, so we give them a little help - final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) - final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) - - // Restore bed spawn - final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world - final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); - final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); - final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); - 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); - - // We start by doing a total reset - entityPlayer.reset(); - entityPlayer.load(TagValueInput.createGlobal(null, 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 - 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.getServer().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; - } - - // Create a BlockPos for the spawn location - BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); - - // Get the dimension ResourceKey from the ServerLevel - ServerLevel serverLevel = craftWorld.getHandle(); - ResourceKey dimension = serverLevel.dimension(); - - // Create a RespawnConfig instance with the required arguments - ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( - dimension, // The dimension of the world - blockPos, // The position to check for respawn - 0.0f, // Yaw (rotation angle), default to 0 - true // Forced flag, set to true to match previous behavior - ); - - // Call the method with the correct arguments - return ServerPlayer.findRespawnAndUseSpawnBlock( - serverLevel, - respawnConfig, - false // Assuming the boolean is respawnAfterDeath - ) - .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-7/build.gradle.kts b/hyperverse-nms-1-21-7/build.gradle.kts deleted file mode 100644 index 3f83cf6..0000000 --- a/hyperverse-nms-1-21-7/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-7/src/main/java/org/incendo/hyperverse/platform/v1_21_7/NMSImpl.java b/hyperverse-nms-1-21-7/src/main/java/org/incendo/hyperverse/platform/v1_21_7/NMSImpl.java deleted file mode 100644 index 41f0a9e..0000000 --- a/hyperverse-nms-1-21-7/src/main/java/org/incendo/hyperverse/platform/v1_21_7/NMSImpl.java +++ /dev/null @@ -1,280 +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.platform.v1_21_7; - -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.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -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.level.storage.TagValueInput; -import net.minecraft.world.level.storage.TagValueOutput; -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(TagValueOutput.createWrappingGlobal(null, playerTag)); - - // Handle the Optional return type of getCompound - CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); - // If "hyperverse" didn't exist, we need to add it to playerTag - playerTag.put("hyperverse", 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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { - // Health and hunger don't update properly, so we give them a little help - final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) - final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) - - // Restore bed spawn - final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world - final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); - final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); - final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); - 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); - - // We start by doing a total reset - entityPlayer.reset(); - entityPlayer.load(TagValueInput.createGlobal(null, 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 - 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.getServer().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; - } - - // Create a BlockPos for the spawn location - BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); - - // Get the dimension ResourceKey from the ServerLevel - ServerLevel serverLevel = craftWorld.getHandle(); - ResourceKey dimension = serverLevel.dimension(); - - // Create a RespawnConfig instance with the required arguments - ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( - dimension, // The dimension of the world - blockPos, // The position to check for respawn - 0.0f, // Yaw (rotation angle), default to 0 - true // Forced flag, set to true to match previous behavior - ); - - // Call the method with the correct arguments - return ServerPlayer.findRespawnAndUseSpawnBlock( - serverLevel, - respawnConfig, - false // Assuming the boolean is respawnAfterDeath - ) - .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-8/build.gradle.kts b/hyperverse-nms-1-21-8/build.gradle.kts deleted file mode 100644 index 680fbb8..0000000 --- a/hyperverse-nms-1-21-8/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java b/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java deleted file mode 100644 index f98a309..0000000 --- a/hyperverse-nms-1-21-8/src/main/java/org/incendo/hyperverse/platform/v1_21_8/NMSImpl.java +++ /dev/null @@ -1,280 +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.platform.v1_21_8; - -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.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -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.level.storage.TagValueInput; -import net.minecraft.world.level.storage.TagValueOutput; -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(TagValueOutput.createWrappingGlobal(null, playerTag)); - - // Handle the Optional return type of getCompound - CompoundTag hyperverse = playerTag.getCompound("hyperverse").orElse(new CompoundTag()); - // If "hyperverse" didn't exist, we need to add it to playerTag - playerTag.put("hyperverse", 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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.getChunkAtAsync(originLocation).thenAccept(chunk -> { - // Health and hunger don't update properly, so we give them a little help - final float health = compound.getFloat("Health").orElse(20.0f); // Default to 20 (full health) - final int foodLevel = compound.getInt("foodLevel").orElse(20); // Default to 20 (full food) - - // Restore bed spawn - final String spawnWorld = compound.getString("SpawnWorld").orElse(player.getWorld().getName()); // Default to current world - final int spawnX = compound.getInt("SpawnX").orElse((int) originLocation.getX()); - final int spawnY = compound.getInt("SpawnY").orElse((int) originLocation.getY()); - final int spawnZ = compound.getInt("SpawnZ").orElse((int) originLocation.getZ()); - 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(TagValueOutput.createWrappingGlobal(null, 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(TagValueOutput.createWrappingGlobal(null, compound)); - - // We start by doing a total reset - entityPlayer.reset(); - entityPlayer.load(TagValueInput.createGlobal(null, 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 - 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.getServer().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; - } - - // Create a BlockPos for the spawn location - BlockPos blockPos = new BlockPos(spawnLocation.getBlockX(), spawnLocation.getBlockY(), spawnLocation.getBlockZ()); - - // Get the dimension ResourceKey from the ServerLevel - ServerLevel serverLevel = craftWorld.getHandle(); - ResourceKey dimension = serverLevel.dimension(); - - // Create a RespawnConfig instance with the required arguments - ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig( - dimension, // The dimension of the world - blockPos, // The position to check for respawn - 0.0f, // Yaw (rotation angle), default to 0 - true // Forced flag, set to true to match previous behavior - ); - - // Call the method with the correct arguments - return ServerPlayer.findRespawnAndUseSpawnBlock( - serverLevel, - respawnConfig, - false // Assuming the boolean is respawnAfterDeath - ) - .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/build.gradle.kts b/hyperverse-nms-1-21/build.gradle.kts deleted file mode 100644 index 80cb781..0000000 --- a/hyperverse-nms-1-21/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("hyperverse.base-conventions") - alias(libs.plugins.paperweight.userdev) -} - -indra { - javaVersions { - minimumToolchain(21) - target(21) - } -} - -dependencies { - paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") - compileOnly(projects.hyperverseNmsCommon) -} - -tasks { - assemble { - dependsOn(reobfJar) - } -} 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 deleted file mode 100644 index ea5f6e4..0000000 --- a/hyperverse-nms-1-21/src/main/java/org/incendo/hyperverse/platform/v1_21/NMSImpl.java +++ /dev/null @@ -1,255 +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.platform.v1_21; - -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.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.isPresent()) { - return; - } - final CompoundTag compound = (CompoundTag) optionalCompound.get(); - PaperLib.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-20-6/build.gradle.kts b/hyperverse-nms-26-1/build.gradle.kts similarity index 54% rename from hyperverse-nms-1-20-6/build.gradle.kts rename to hyperverse-nms-26-1/build.gradle.kts index 1e72188..dbeb849 100644 --- a/hyperverse-nms-1-20-6/build.gradle.kts +++ b/hyperverse-nms-26-1/build.gradle.kts @@ -5,18 +5,12 @@ plugins { indra { javaVersions { - minimumToolchain(21) - target(21) + minimumToolchain(25) + target(25) } } dependencies { - paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") + paperweight.paperDevBundle("26.1.2.build.+") compileOnly(projects.hyperverseNmsCommon) } - -tasks { - assemble { - dependsOn(reobfJar) - } -} diff --git a/hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java b/hyperverse-nms-26-1/src/main/java/org/incendo/hyperverse/platform/v26_1_2/NMSImpl.java similarity index 98% rename from hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java rename to hyperverse-nms-26-1/src/main/java/org/incendo/hyperverse/platform/v26_1_2/NMSImpl.java index 630020f..233178f 100644 --- a/hyperverse-nms-1-21-11/src/main/java/org/incendo/hyperverse/platform/v1_21_11/NMSImpl.java +++ b/hyperverse-nms-26-1/src/main/java/org/incendo/hyperverse/platform/v26_1_2/NMSImpl.java @@ -15,7 +15,7 @@ // along with this program. If not, see . // -package org.incendo.hyperverse.platform.v1_21_11; +package org.incendo.hyperverse.platform.v26_1_2; import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; @@ -31,12 +31,9 @@ import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.IntTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; diff --git a/hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java b/hyperverse-nms-26-2/src/main/java/org.incendo.hyperverse.platform.v26_2/NMSImpl.java similarity index 98% rename from hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java rename to hyperverse-nms-26-2/src/main/java/org.incendo.hyperverse.platform.v26_2/NMSImpl.java index a7b3909..693129f 100644 --- a/hyperverse-nms-1-21-10/src/main/java/org/incendo/hyperverse/platform/v1_21_10/NMSImpl.java +++ b/hyperverse-nms-26-2/src/main/java/org.incendo.hyperverse.platform.v26_2/NMSImpl.java @@ -15,7 +15,7 @@ // along with this program. If not, see . // -package org.incendo.hyperverse.platform.v1_21_10; +package org.incendo.hyperverse.platform.v26_2; import co.aikar.taskchain.TaskChainFactory; import com.google.inject.Inject; @@ -28,19 +28,16 @@ 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.IntTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.StringTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.BlockUtil; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.border.WorldBorder; diff --git a/settings.gradle.kts b/settings.gradle.kts index 411ddbf..3b7334f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { repositories { gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") } includeBuild("gradle/build-logic") } @@ -13,13 +14,5 @@ include(":hyperverse-nms-common") include(":hyperverse-core") include(":hyperverse-nms-unsupported") -include(":hyperverse-nms-1-20-6") -include(":hyperverse-nms-1-21") -include(":hyperverse-nms-1-21-3") -include(":hyperverse-nms-1-21-4") -include(":hyperverse-nms-1-21-5") -include(":hyperverse-nms-1-21-6") -include(":hyperverse-nms-1-21-7") -include(":hyperverse-nms-1-21-8") -include(":hyperverse-nms-1-21-10") -include(":hyperverse-nms-1-21-11") +include(":hyperverse-nms-26-1") +include(":hyperverse-nms-26-2") From c9c9725bb474e08631f0c70045e524abcde6fb56 Mon Sep 17 00:00:00 2001 From: Snewmy Date: Thu, 16 Jul 2026 16:59:22 -0500 Subject: [PATCH 5/5] Add 26.2 support --- hyperverse-nms-26-2/build.gradle.kts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 hyperverse-nms-26-2/build.gradle.kts diff --git a/hyperverse-nms-26-2/build.gradle.kts b/hyperverse-nms-26-2/build.gradle.kts new file mode 100644 index 0000000..b88e23b --- /dev/null +++ b/hyperverse-nms-26-2/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("hyperverse.base-conventions") + alias(libs.plugins.paperweight.userdev) +} + +indra { + javaVersions { + minimumToolchain(25) + target(25) + } +} + +dependencies { + paperweight.paperDevBundle("26.2.build.+") + compileOnly(projects.hyperverseNmsCommon) +}