Sky fall when respawning in purg, rest works
This commit is contained in:
@@ -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<TeleportRequest> teleportQueue = new ArrayList<>();
|
||||
if (persistentData.contains("PurgatorySavedSpawn")) {
|
||||
CompoundTag purgatoryData = persistentData.getCompound("PurgatorySavedSpawn");
|
||||
|
||||
public static void teleportToDimension(ServerPlayer player, ResourceKey<Level> 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<Level> 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<TeleportRequest> 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());
|
||||
// Restore original bed/anchor spawn
|
||||
player.setRespawnPosition(originalDim, originalBedPos, angle, forced, false);
|
||||
} else {
|
||||
teleportQueue.add(request);
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Level> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user