diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/listeners/TownyPlayerListener.java b/Towny/src/main/java/com/palmergames/bukkit/towny/listeners/TownyPlayerListener.java index af906591c2..97543fc5fb 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/listeners/TownyPlayerListener.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/listeners/TownyPlayerListener.java @@ -215,11 +215,16 @@ public void onPlayerRespawn(PlayerRespawnEvent event) { Resident resident = TownyAPI.getInstance().getResident(player); // Towny or the Resident might be prioritizing bed spawns over town spawns. - if (TownySettings.getBedUse() || - (resident != null && resident.hasMode("bedspawn"))) { - Location bed = BukkitTools.getBedOrRespawnLocation(player); - if (bed != null) - respawn = bed; + if (TownySettings.getBedUse() || (resident != null && resident.hasMode("bedspawn"))) { + // Get the potential respawn location first to check the world + final Location potentialLocation = TownySettings.isTownRespawningInOtherWorlds() ? null : BukkitTools.getPotentialRespawnLocation(player); + + if (potentialLocation == null || player.getWorld().equals(potentialLocation.getWorld())) { + + Location respawnBlock = BukkitTools.findRespawnBlock(player); + if (respawnBlock != null) + respawn = respawnBlock; + } } // Town spawn could be null and no bed was available. diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/tasks/CleanupTask.java b/Towny/src/main/java/com/palmergames/bukkit/towny/tasks/CleanupTask.java index ad49d7e3dc..e46a4c6d14 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/tasks/CleanupTask.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/tasks/CleanupTask.java @@ -27,9 +27,13 @@ private void cleanupBackups() { long deleteAfter = TownySettings.getBackupLifeLength(); if (deleteAfter >= 0) { + File backupDirectory = new File(TownyUniverse.getInstance().getRootFolder() + File.separator + "backup"); + if (!backupDirectory.exists()) + return; + Towny.getPlugin().getLogger().info("Cleaning up old backups..."); - if (FileMgmt.deleteOldBackups(new File(TownyUniverse.getInstance().getRootFolder() + File.separator + "backup"), deleteAfter)) + if (FileMgmt.deleteOldBackups(backupDirectory, deleteAfter)) Towny.getPlugin().getLogger().info("Successfully cleaned backups."); else Towny.getPlugin().getLogger().info("Could not delete old backups."); diff --git a/Towny/src/main/java/com/palmergames/bukkit/util/BukkitTools.java b/Towny/src/main/java/com/palmergames/bukkit/util/BukkitTools.java index 9429fbf0d4..10765da598 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/util/BukkitTools.java +++ b/Towny/src/main/java/com/palmergames/bukkit/util/BukkitTools.java @@ -8,6 +8,7 @@ import com.palmergames.bukkit.towny.hooks.PluginIntegrations; import com.palmergames.bukkit.towny.utils.MinecraftVersion; +import com.palmergames.util.JavaUtil; import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.Location; @@ -19,6 +20,7 @@ import org.bukkit.World; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; @@ -33,7 +35,6 @@ import org.jetbrains.annotations.Nullable; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; @@ -44,6 +45,8 @@ import java.util.Locale; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; import java.util.stream.Collectors; /** @@ -58,6 +61,7 @@ public class BukkitTools { @SuppressWarnings("unused") private static Towny plugin = null; private static final MethodHandle GET_OFFLINE_PLAYER_CACHED; + private static final MethodHandle GET_POTENTIAL_RESPAWN_LOCATION; public static void initialize(Towny plugin) { BukkitTools.plugin = plugin; @@ -267,9 +271,41 @@ public static List getWorldNames() { public static List getWorldNames(boolean lowercased) { return lowercased ? getWorlds().stream().map(world -> world.getName().toLowerCase(Locale.ROOT)).collect(Collectors.toList()) : getWorldNames(); } + + public static Location findRespawnBlock(Player player) { + final Location potentialLocation = getPotentialRespawnLocation(player); + + if (potentialLocation != null && !plugin.getScheduler().isRegionThread(potentialLocation)) { + // Getting the respawn location can load chunks, so we have to make sure we're on the region thread for the potential location + + CompletableFuture future = new CompletableFuture<>(); + plugin.getScheduler().run(potentialLocation, () -> { + try { + future.complete(getRespawnLocation(player)); + } catch (Throwable throwable) { + plugin.getLogger().log(Level.SEVERE, "Exception occurred while getting respawn location", throwable); + future.completeExceptionally(throwable); + } + }); + + return future.join(); + } + + return getRespawnLocation(player); + } + + @Nullable + public static Location getPotentialRespawnLocation(HumanEntity player) { + try { + return (Location) GET_POTENTIAL_RESPAWN_LOCATION.invokeExact(player); + } catch (Throwable throwable) { + plugin.getLogger().log(Level.SEVERE, "Exception occurred while getting potential respawn location", throwable); // TODO: remove + return null; + } + } @SuppressWarnings("deprecation") - public static Location getBedOrRespawnLocation(Player player) { + public static Location getRespawnLocation(Player player) { return MinecraftVersion.CURRENT_VERSION.isOlderThanOrEquals(MinecraftVersion.MINECRAFT_1_20_3) ? player.getBedSpawnLocation() : player.getRespawnLocation(); } @@ -371,12 +407,7 @@ public static Collection convertKeyedToString(@NotNull Collection