Implement proper end exit portal handling.

When players will exit the end-gate, they will be teleported to proper island, instead of world spawn point.

The only way how players can be teleported to the spawn is if they do not have islands.
This commit is contained in:
BONNe 2022-09-30 02:25:44 +03:00
parent eb5147e710
commit 97c4cf883f
2 changed files with 99 additions and 21 deletions

View File

@ -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<Island> 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<Island> 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<UUID> inPortal;
/**
* Map that links entities origin of teleportation. Used for respawning.
*/
protected final Map<UUID, World> teleportOrigin;
/**
* Set of entities that currently is in teleportation.
*/

View File

@ -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);
}