From 24242cc895673f9b43ac7e49966c021e354957c9 Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 8 Sep 2026 00:38:57 +0200 Subject: [PATCH] Sky fall when respawning in purg, rest works --- .../dimension/DimensionTeleporter.java | 83 +++++------- .../dimension/PurgatoryEventHandler.java | 123 ++++++++++++++++-- 2 files changed, 143 insertions(+), 63 deletions(-) diff --git a/src/main/java/dev/lazysucksat/purgatory/dimension/DimensionTeleporter.java b/src/main/java/dev/lazysucksat/purgatory/dimension/DimensionTeleporter.java index 38f8b88..94aa42b 100644 --- a/src/main/java/dev/lazysucksat/purgatory/dimension/DimensionTeleporter.java +++ b/src/main/java/dev/lazysucksat/purgatory/dimension/DimensionTeleporter.java @@ -4,7 +4,10 @@ import com.mojang.logging.LogUtils; import dev.lazysucksat.purgatory.Purgatory; import net.minecraft.core.BlockPos; import net.minecraft.core.Position; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -17,64 +20,46 @@ import net.minecraftforge.fml.common.Mod; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Mod.EventBusSubscriber(modid = Purgatory.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE) public class DimensionTeleporter { - private record TeleportRequest(ServerPlayer player, ServerLevel targetLevel) {} + public static void escapePurgatory(ServerPlayer player) { + CompoundTag persistentData = player.getPersistentData(); - private static final ArrayList teleportQueue = new ArrayList<>(); + if (persistentData.contains("PurgatorySavedSpawn")) { + CompoundTag purgatoryData = persistentData.getCompound("PurgatorySavedSpawn"); - public static void teleportToDimension(ServerPlayer player, ResourceKey dimensionKey) { - if (player.level().isClientSide() && player.getServer() != null) { - return; - } + if (purgatoryData.getBoolean("HasSavedBed")) { + BlockPos originalBedPos = new BlockPos( + purgatoryData.getInt("BedX"), + purgatoryData.getInt("BedY"), + purgatoryData.getInt("BedZ") + ); - MinecraftServer server = player.getServer(); - assert server != null; - ServerLevel targetLevel = server.getLevel(dimensionKey); + ResourceKey originalDim = ResourceKey.create( + Registries.DIMENSION, + ResourceLocation.parse(purgatoryData.getString("BedDim")) + ); - if (targetLevel == null) { - LogUtils.getLogger().atError().log(String.format("Dimension %s does not exist!", dimensionKey.toString())); - return; - } + float angle = purgatoryData.getFloat("BedAngle"); + boolean forced = purgatoryData.getBoolean("BedForced"); - teleportQueue.add(new TeleportRequest(player, targetLevel)); - } - - @SubscribeEvent - public static void onServerTick(TickEvent.ServerTickEvent event) { - if (event.phase == TickEvent.Phase.END && !teleportQueue.isEmpty()) { - - List toTeleport = new ArrayList<>(teleportQueue); - teleportQueue.clear(); - - LogUtils.getLogger().atInfo().log("Teleporting players!"); - - for (TeleportRequest request : toTeleport) { - ServerPlayer player = request.player(); - ServerLevel target = request.targetLevel(); - - double currentScale = player.level().dimensionType().coordinateScale(); - double destScale = target.dimensionType().coordinateScale(); - - double scaleFactor = currentScale / destScale; - - double targetX = player.getX() * scaleFactor; - double targetZ = player.getZ() * scaleFactor; - - int blockX = (int) targetX; - int blockZ = (int) targetZ; - - BlockPos highestPos = target.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, new BlockPos(blockX, 0, blockZ)); - - int highestY = highestPos.getY(); - - if (player.isAlive()) { - player.teleportTo(target, targetX, highestY, targetZ, player.getYRot(), player.getXRot()); - } else { - teleportQueue.add(request); - } + // Restore original bed/anchor spawn + player.setRespawnPosition(originalDim, originalBedPos, angle, forced, false); + } else { + // Clear custom spawn so they fall back to Overworld world spawn + player.setRespawnPosition(Level.OVERWORLD, null, 0.0F, false, false); } + + // Clean up NBT tag + persistentData.remove("PurgatorySavedSpawn"); + } + + // Teleport player out back to Overworld or their bed + ServerLevel overworld = Objects.requireNonNull(player.getServer()).getLevel(Level.OVERWORLD); + if (overworld != null) { + player.changeDimension(overworld, new PurgatoryTeleport()); } } } diff --git a/src/main/java/dev/lazysucksat/purgatory/dimension/PurgatoryEventHandler.java b/src/main/java/dev/lazysucksat/purgatory/dimension/PurgatoryEventHandler.java index 4c1777d..d274a2e 100644 --- a/src/main/java/dev/lazysucksat/purgatory/dimension/PurgatoryEventHandler.java +++ b/src/main/java/dev/lazysucksat/purgatory/dimension/PurgatoryEventHandler.java @@ -1,13 +1,14 @@ package dev.lazysucksat.purgatory.dimension; -import com.mojang.logging.LogUtils; import dev.lazysucksat.purgatory.Purgatory; -import dev.lazysucksat.purgatory.players.PlayerChecks; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraftforge.common.MinecraftForge; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.levelgen.Heightmap; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -17,16 +18,110 @@ import net.minecraftforge.fml.common.Mod; public class PurgatoryEventHandler { @SubscribeEvent - public static void respawnEventHandler(PlayerEvent.PlayerRespawnEvent respawnEvent) { - LogUtils.getLogger().atInfo().log("Ran respawnEventHandler"); - Player player = respawnEvent.getEntity(); - - if (!PlayerChecks.playerUnlockedPurgatory(player)) { + public static void onPlayerDeath(LivingDeathEvent event) { + if (!(event.getEntity() instanceof ServerPlayer player)) { return; } - if (player instanceof ServerPlayer serverPlayer) { - DimensionTeleporter.teleportToDimension(serverPlayer, PurgatoryDim.PURGATORY_KEY); + // Only redirect death if dying outside Purgatory + if (!player.level().dimension().equals(PurgatoryDim.PURGATORY_KEY)) { + + // 1. Save original Bed/Anchor info into player persistent NBT + BlockPos bedPos = player.getRespawnPosition(); + ResourceKey bedDim = player.getRespawnDimension(); + float bedAngle = player.getRespawnAngle(); + boolean bedForced = player.isRespawnForced(); + + CompoundTag persistentData = player.getPersistentData(); + CompoundTag purgatoryData = new CompoundTag(); + + if (bedPos != null) { + purgatoryData.putInt("BedX", bedPos.getX()); + purgatoryData.putInt("BedY", bedPos.getY()); + purgatoryData.putInt("BedZ", bedPos.getZ()); + purgatoryData.putString("BedDim", bedDim.location().toString()); + purgatoryData.putFloat("BedAngle", bedAngle); + purgatoryData.putBoolean("BedForced", bedForced); + purgatoryData.putBoolean("HasSavedBed", true); + } else { + purgatoryData.putBoolean("HasSavedBed", false); + } + + ServerLevel purgatoryLevel = player.getServer().getLevel(PurgatoryDim.PURGATORY_KEY); + if (purgatoryLevel == null) return; + + double currentScale = player.level().dimensionType().coordinateScale(); + double destScale = purgatoryLevel.dimensionType().coordinateScale(); + double scaleFactor = currentScale / destScale; + + int targetX = (int) (player.getX() * scaleFactor); + int targetZ = (int) (player.getZ() * scaleFactor); + + // 2. TRICK: Target the sky limit. + // Vanilla natively accepts Air as a valid forced spawn. This prevents Vanilla from + // rejecting the spawn due to unloaded chunks and stops the "missing bed" error. + int safeSkyY = purgatoryLevel.getMaxBuildHeight() - 1; + BlockPos skySpawn = new BlockPos(targetX, safeSkyY, targetZ); + + purgatoryData.putBoolean("NeedsGroundSnap", true); + persistentData.put("PurgatorySavedSpawn", purgatoryData); + + // Start loading the chunk early while the player is on the death screen + purgatoryLevel.getChunkSource().addRegionTicket( + net.minecraft.server.level.TicketType.POST_TELEPORT, + new ChunkPos(skySpawn), + 3, + player.getId() + ); + + // Force vanilla to natively respawn them in Purgatory + player.setRespawnPosition(PurgatoryDim.PURGATORY_KEY, skySpawn, player.getYRot(), true, false); } } -} + + @SubscribeEvent + public static void onPlayerClone(PlayerEvent.Clone event) { + if (!event.isWasDeath()) { + return; + } + + // Preserve Purgatory NBT data across the respawn player instance creation + CompoundTag oldData = event.getOriginal().getPersistentData(); + if (oldData.contains("PurgatorySavedSpawn")) { + event.getEntity().getPersistentData().put("PurgatorySavedSpawn", oldData.getCompound("PurgatorySavedSpawn").copy()); + } + } + + @SubscribeEvent + public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) { + if (!(event.getEntity() instanceof ServerPlayer player) || event.isEndConquered()) { + return; + } + + CompoundTag persistentData = player.getPersistentData(); + if (persistentData.contains("PurgatorySavedSpawn")) { + CompoundTag purgatoryData = persistentData.getCompound("PurgatorySavedSpawn"); + + if (purgatoryData.getBoolean("NeedsGroundSnap") && player.level().dimension().equals(PurgatoryDim.PURGATORY_KEY)) { + // Consume the snap flag so it only happens once + purgatoryData.putBoolean("NeedsGroundSnap", false); + + ServerLevel purgatoryLevel = (ServerLevel) player.level(); + BlockPos currentPos = player.blockPosition(); + + // Now that the player is natively inside Purgatory, the chunk is GUARANTEED + // to be fully loaded, meaning getHeightmapPos will return the true surface level. + BlockPos surfacePos = purgatoryLevel.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, currentPos); + + // Instantly snap the player down to the surface before the client renders + player.teleportTo( + surfacePos.getX() + 0.5, + surfacePos.getY(), + surfacePos.getZ() + 0.5 + ); + + player.fallDistance = 0.0F; // Prevent fall damage from the snap + } + } + } +} \ No newline at end of file