diff --git a/src/main/java/world/bentobox/bentobox/listeners/teleports/AbstractTeleportListener.java b/src/main/java/world/bentobox/bentobox/listeners/teleports/AbstractTeleportListener.java index e2985d6e1..fa618b899 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/teleports/AbstractTeleportListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/teleports/AbstractTeleportListener.java @@ -11,12 +11,10 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.entity.Player; import org.eclipse.jdt.annotation.NonNull; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import org.eclipse.jdt.annotation.Nullable; +import java.util.*; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; @@ -37,6 +35,7 @@ public abstract class AbstractTeleportListener this.plugin = bentoBox; this.inPortal = new HashSet<>(); this.inTeleport = new HashSet<>(); + this.teleportOrigin = new HashMap<>(); } @@ -51,7 +50,17 @@ public abstract class AbstractTeleportListener */ protected Optional getIsland(Location location) { - return this.plugin.getIslands().getProtectedIslandAt(location); + return this.plugin.getIslandsManager().getProtectedIslandAt(location); + } + + + /** + * Get island for given player at the given world. + * @return optional island at given world. + */ + protected Optional getIsland(World world, Player player) + { + return Optional.ofNullable(this.plugin.getIslandsManager().getIsland(world, player.getUniqueId())); } @@ -260,6 +269,23 @@ public abstract class AbstractTeleportListener } + /** + * This method returns spawn location for given world. + * @param world World which spawn point must be returned. + * @return Spawn location for world or null. + */ + @Nullable + protected Location getSpawnLocation(World world) + { + return this.plugin.getIslandsManager().getSpawn(world).map(island -> + island.getSpawnPoint(World.Environment.NORMAL) == null ? + island.getCenter() : + island.getSpawnPoint(World.Environment.NORMAL)). + orElse(this.plugin.getIslands().isSafeLocation(world.getSpawnLocation()) ? + world.getSpawnLocation() : null); + } + + /** * This method returns if missing islands should be generated uppon teleportation. * Can happen only in non-custom generators. @@ -289,6 +315,11 @@ public abstract class AbstractTeleportListener */ protected final Set inPortal; + /** + * Map that links entities origin of teleportation. Used for respawning. + */ + protected final Map teleportOrigin; + /** * Set of entities that currently is in teleportation. */ diff --git a/src/main/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListener.java b/src/main/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListener.java index f91eafb22..2759946b4 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListener.java @@ -20,6 +20,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; @@ -101,6 +102,8 @@ public class PlayerTeleportListener extends AbstractTeleportListener implements } this.inPortal.add(uuid); + // Add original world for respawning. + this.teleportOrigin.put(uuid, event.getLocation().getWorld()); if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL)) { @@ -160,10 +163,65 @@ public class PlayerTeleportListener extends AbstractTeleportListener implements // Player exits nether portal. this.inPortal.remove(event.getPlayer().getUniqueId()); this.inTeleport.remove(event.getPlayer().getUniqueId()); + this.teleportOrigin.remove(event.getPlayer().getUniqueId()); } } + /** + * Player respawn event is triggered when player enters exit portal at the end. + * This will take over respawn mechanism and place player on island. + * @param event player respawn event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerExitPortal(PlayerRespawnEvent event) + { + if (!this.teleportOrigin.containsKey(event.getPlayer().getUniqueId())) + { + // Player is already processed. + return; + } + + World fromWorld = this.teleportOrigin.get(event.getPlayer().getUniqueId()); + World overWorld = Util.getWorld(fromWorld); + + if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld)) + { + // Not teleporting from/to bentobox worlds. + return; + } + + this.getIsland(overWorld, event.getPlayer()).ifPresentOrElse(island -> { + if (!island.onIsland(event.getRespawnLocation())) + { + // If respawn location is outside island protection range, change location to the + // spawn in overworld or home location. + Location location = island.getSpawnPoint(World.Environment.NORMAL); + + if (location == null) + { + // No spawn point. Rare thing. Well, use island protection center. + location = island.getProtectionCenter(); + } + + event.setRespawnLocation(location); + } + }, + () -> { + // Player does not an island. Try to get spawn island, and if that fails, use world spawn point. + // If spawn point is not safe, do nothing. Let server handle it. + + Location spawnLocation = this.getSpawnLocation(overWorld); + + if (spawnLocation != null) + { + event.setRespawnLocation(spawnLocation); + } + }); + } + + + // --------------------------------------------------------------------- // Section: Processors // --------------------------------------------------------------------- @@ -365,21 +423,10 @@ public class PlayerTeleportListener extends AbstractTeleportListener implements else { // Cannot be portal. Should recalculate position. - - Location toLocation; - Island island = this.plugin.getIslandsManager().getIsland(overWorld, event.getPlayer().getUniqueId()); - - if (island == null) - { - // What to do? Player do not have an island! Check for spawn? - // TODO: SPAWN CHECK. - toLocation = event.getFrom(); - } - else - { - // TODO: Island Respawn, Bed, Default home location check. - toLocation = island.getSpawnPoint(World.Environment.NORMAL); - } + // TODO: Currently, it is always spawn location. However, default home must be assigned. + Location toLocation = this.getIsland(overWorld, event.getPlayer()). + map(island -> island.getSpawnPoint(World.Environment.NORMAL)). + orElse(event.getFrom()); event.setTo(toLocation); }