422 lines
15 KiB
Java
422 lines
15 KiB
Java
//
|
|
// Created by BONNe
|
|
// Copyright - 2022
|
|
//
|
|
|
|
|
|
package world.bentobox.bentobox.listeners.teleports;
|
|
|
|
|
|
import java.util.Objects;
|
|
import java.util.UUID;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.World;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.EntityType;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.entity.EntityPortalEnterEvent;
|
|
import org.bukkit.event.entity.EntityPortalEvent;
|
|
import org.bukkit.event.entity.EntityPortalExitEvent;
|
|
import org.bukkit.util.Vector;
|
|
import org.eclipse.jdt.annotation.NonNull;
|
|
|
|
import world.bentobox.bentobox.BentoBox;
|
|
import world.bentobox.bentobox.lists.Flags;
|
|
import world.bentobox.bentobox.util.Util;
|
|
import world.bentobox.bentobox.util.teleport.ClosestSafeSpotTeleport;
|
|
|
|
|
|
/**
|
|
* This class handles entity teleportation between dimensions.
|
|
*
|
|
* @author BONNe
|
|
*/
|
|
public class EntityTeleportListener extends AbstractTeleportListener implements Listener
|
|
{
|
|
/**
|
|
* Instance of Teleportation processor.
|
|
*
|
|
* @param bentoBox BentoBox plugin.
|
|
*/
|
|
public EntityTeleportListener(@NonNull BentoBox bentoBox)
|
|
{
|
|
super(bentoBox);
|
|
}
|
|
|
|
|
|
/**
|
|
* This listener checks entity portal events and triggers appropriate methods to transfer
|
|
* entities to the correct location in other dimension.
|
|
* <p>
|
|
* This event is triggered when entity is about to being teleported because of contact with the
|
|
* nether portal or end gateway portal (exit portal triggers respawn).
|
|
* <p>
|
|
* This event is not called if nether/end is disabled in server settings.
|
|
*
|
|
* @param event the entity portal event.
|
|
*/
|
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
|
public void onEntityPortal(EntityPortalEvent event)
|
|
{
|
|
World fromWorld = event.getFrom().getWorld();
|
|
World overWorld = Util.getWorld(fromWorld);
|
|
|
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld) || event.getTo() == null)
|
|
{
|
|
// Not a bentobox world.
|
|
return;
|
|
}
|
|
|
|
if (!Flags.ENTITY_PORTAL_TELEPORT.isSetForWorld(overWorld))
|
|
{
|
|
// Teleportation is disabled. Cancel event.
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
|
|
// Check which teleportation is happening.
|
|
|
|
World.Environment source = fromWorld.getEnvironment();
|
|
World.Environment destination = event.getTo().getWorld().getEnvironment();
|
|
|
|
if (World.Environment.NETHER == source && World.Environment.NORMAL == destination ||
|
|
World.Environment.NORMAL == source && World.Environment.NETHER == destination)
|
|
{
|
|
// Nether to overworld or opposite
|
|
this.portalProcess(event, World.Environment.NETHER);
|
|
}
|
|
else if (World.Environment.THE_END == source && World.Environment.NORMAL == destination ||
|
|
World.Environment.NORMAL == source && World.Environment.THE_END == destination)
|
|
{
|
|
// end to overworld or opposite
|
|
this.portalProcess(event, World.Environment.THE_END);
|
|
}
|
|
else
|
|
{
|
|
// unknown teleportation
|
|
this.portalProcess(event, event.getTo().getWorld().getEnvironment());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Fires the event if nether or end is disabled at the system level
|
|
*
|
|
* @param event - EntityPortalEnterEvent
|
|
*/
|
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
|
public void onEntityEnterPortal(EntityPortalEnterEvent event)
|
|
{
|
|
if (EntityType.PLAYER.equals(event.getEntity().getType()))
|
|
{
|
|
// This handles only non-players.
|
|
return;
|
|
}
|
|
|
|
Entity entity = event.getEntity();
|
|
Material type = event.getLocation().getBlock().getType();
|
|
UUID uuid = entity.getUniqueId();
|
|
|
|
if (this.inPortal.contains(uuid))
|
|
{
|
|
// Already in process.
|
|
return;
|
|
}
|
|
|
|
World fromWorld = event.getLocation().getWorld();
|
|
World overWorld = Util.getWorld(fromWorld);
|
|
|
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld))
|
|
{
|
|
// Not a bentobox world.
|
|
return;
|
|
}
|
|
|
|
if (!Flags.ENTITY_PORTAL_TELEPORT.isSetForWorld(overWorld))
|
|
{
|
|
// Teleportation is disabled. Cancel processing.
|
|
return;
|
|
}
|
|
|
|
this.inPortal.add(uuid);
|
|
// Add original world for respawning.
|
|
this.teleportOrigin.put(uuid, fromWorld);
|
|
|
|
// Entities are teleported instantly.
|
|
if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL))
|
|
{
|
|
if (fromWorld == overWorld)
|
|
{
|
|
this.portalProcess(
|
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
|
World.Environment.NETHER);
|
|
}
|
|
else
|
|
{
|
|
this.portalProcess(
|
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
|
World.Environment.NORMAL);
|
|
}
|
|
|
|
// Do not process anything else.
|
|
return;
|
|
}
|
|
|
|
// Entities are teleported instantly.
|
|
if (!Bukkit.getAllowEnd() && (type.equals(Material.END_PORTAL) || type.equals(Material.END_GATEWAY)))
|
|
{
|
|
if (fromWorld == this.getNetherEndWorld(overWorld, World.Environment.THE_END))
|
|
{
|
|
this.portalProcess(
|
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
|
World.Environment.NORMAL);
|
|
}
|
|
else
|
|
{
|
|
this.portalProcess(
|
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
|
World.Environment.THE_END);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove inPortal flag only when entity exits the portal
|
|
*
|
|
* @param event entity move event
|
|
*/
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
public void onEntityExitPortal(EntityPortalExitEvent event)
|
|
{
|
|
if (!this.inPortal.contains(event.getEntity().getUniqueId()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.inPortal.remove(event.getEntity().getUniqueId());
|
|
this.inTeleport.remove(event.getEntity().getUniqueId());
|
|
this.teleportOrigin.remove(event.getEntity().getUniqueId());
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Section: Methods
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
* This method process entity teleportation to a correct dimension.
|
|
* @param event Event that triggers teleportation.
|
|
* @param environment Environment of the dimension where entity must appear.
|
|
*/
|
|
private void portalProcess(EntityPortalEvent event, World.Environment environment)
|
|
{
|
|
World fromWorld = event.getFrom().getWorld();
|
|
World overWorld = Util.getWorld(fromWorld);
|
|
|
|
if (fromWorld == null || overWorld == null)
|
|
{
|
|
// Missing worlds.
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
|
|
if (!this.isAllowedInConfig(overWorld, environment))
|
|
{
|
|
// World is disabled in config. Do not teleport player.
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
|
|
if (!this.isAllowedOnServer(environment))
|
|
{
|
|
// World is disabled in bukkit. Event is not triggered, but cancel just in case.
|
|
event.setCancelled(true);
|
|
}
|
|
if (this.inTeleport.contains(event.getEntity().getUniqueId()))
|
|
{
|
|
// Entity is already in teleportation.
|
|
return;
|
|
}
|
|
this.inTeleport.add(event.getEntity().getUniqueId());
|
|
|
|
if (fromWorld.equals(overWorld) && !this.isIslandWorld(overWorld, environment))
|
|
{
|
|
// This is not island world. Use standard nether or end world teleportation.
|
|
this.handleToStandardNetherOrEnd(event, overWorld, environment);
|
|
return;
|
|
}
|
|
|
|
if (!fromWorld.equals(overWorld) && !this.isIslandWorld(overWorld, environment))
|
|
{
|
|
// If entering a portal in the other world, teleport to a portal in overworld if
|
|
// there is one
|
|
this.handleFromStandardNetherOrEnd(event, overWorld, environment);
|
|
return;
|
|
}
|
|
|
|
// To the nether/end or overworld.
|
|
World toWorld = !fromWorld.getEnvironment().equals(environment) ?
|
|
this.getNetherEndWorld(overWorld, environment) : overWorld;
|
|
|
|
// Set the destination location
|
|
// If portals cannot be created, then destination is the spawn point, otherwise it's the vector
|
|
event.setTo(this.calculateLocation(event.getFrom(),
|
|
fromWorld,
|
|
toWorld,
|
|
environment,
|
|
this.isMakePortals(overWorld, environment)));
|
|
|
|
// Calculate search radius for portal
|
|
this.getIsland(event.getTo()).ifPresent(island ->
|
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
|
|
|
// Check if there is an island there or not
|
|
if (this.isPastingMissingIslands(overWorld) &&
|
|
this.isAllowedInConfig(overWorld, environment) &&
|
|
this.isIslandWorld(overWorld, environment) &&
|
|
this.getNetherEndWorld(overWorld, environment) != null &&
|
|
this.getIsland(event.getTo()).
|
|
filter(island -> !this.hasPartnerIsland(island, environment)).
|
|
map(island -> {
|
|
event.setCancelled(true);
|
|
return true;
|
|
}).
|
|
orElse(false))
|
|
{
|
|
// If there is no island, then processor already entity cannot be teleported before player
|
|
// visit that dimension.
|
|
return;
|
|
}
|
|
|
|
if (!event.isCancelled())
|
|
{
|
|
// Let the server teleport
|
|
return;
|
|
}
|
|
|
|
if (environment.equals(World.Environment.THE_END))
|
|
{
|
|
// Prevent death from hitting the ground while calculating location.
|
|
event.getEntity().setVelocity(new Vector(0,0,0));
|
|
event.getEntity().setFallDistance(0);
|
|
}
|
|
|
|
// If we do not generate portals, teleportation should happen manually with safe spot builder.
|
|
// Otherwise, we could end up with situations when player is placed in mid air, if teleportation
|
|
// is done instantly.
|
|
// Our safe spot task is triggered in next tick, however, end teleportation happens in the same tick.
|
|
// It is placed outside THE_END check, as technically it could happen with the nether portal too.
|
|
|
|
// If there is a portal to go to already, then the player will go there
|
|
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
|
if (!event.getEntity().getWorld().equals(toWorld))
|
|
{
|
|
// Else manually teleport entity
|
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
|
entity(event.getEntity()).
|
|
location(event.getTo()).
|
|
portal().
|
|
successRunnable(() -> {
|
|
// Reset velocity just in case.
|
|
event.getEntity().setVelocity(new Vector(0,0,0));
|
|
event.getEntity().setFallDistance(0);
|
|
}).
|
|
build();
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle teleport to standard nether or end
|
|
* @param event - EntityPortalEvent
|
|
* @param overWorld - over world
|
|
* @param environment - to target environment
|
|
*/
|
|
private void handleToStandardNetherOrEnd(EntityPortalEvent event, World overWorld, World.Environment environment)
|
|
{
|
|
World toWorld = Objects.requireNonNull(this.getNetherEndWorld(overWorld, environment));
|
|
Location spawnPoint = toWorld.getSpawnLocation();
|
|
|
|
// If going to the nether and nether portals are active then just teleport to approx location
|
|
if (World.Environment.NETHER.equals(toWorld.getEnvironment()) &&
|
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
|
{
|
|
spawnPoint = event.getFrom().toVector().toLocation(toWorld);
|
|
}
|
|
|
|
// If spawn is set as 0,63,0 in the End then move it to 100, 50 ,0.
|
|
if (World.Environment.THE_END.equals(toWorld.getEnvironment()) && spawnPoint.getBlockX() == 0 && spawnPoint.getBlockZ() == 0)
|
|
{
|
|
// Set to the default end spawn
|
|
spawnPoint = new Location(toWorld, 100, 50, 0);
|
|
toWorld.setSpawnLocation(100, 50, 0);
|
|
}
|
|
|
|
if (this.isAllowedOnServer(environment))
|
|
{
|
|
// To Standard Nether or end
|
|
event.setTo(spawnPoint);
|
|
}
|
|
else
|
|
{
|
|
// Teleport to standard nether or end
|
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
|
entity(event.getEntity()).
|
|
location(spawnPoint).
|
|
portal().
|
|
build();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle teleport from standard nether or end
|
|
* @param event - EntityPortalEvent
|
|
* @param overWorld - over world
|
|
* @param environment - to world environment
|
|
*/
|
|
private void handleFromStandardNetherOrEnd(EntityPortalEvent event, World overWorld, World.Environment environment)
|
|
{
|
|
if (World.Environment.NETHER.equals(environment) &&
|
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
|
{
|
|
// Set to location directly to the from location.
|
|
event.setTo(event.getFrom().toVector().toLocation(overWorld));
|
|
|
|
// Update portal search radius.
|
|
this.getIsland(event.getTo()).ifPresent(island ->
|
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
|
}
|
|
else
|
|
{
|
|
// Cannot be portal. Should recalculate position.
|
|
Location spawnLocation = this.getSpawnLocation(overWorld);
|
|
|
|
event.setTo(spawnLocation == null ?
|
|
event.getFrom().toVector().toLocation(overWorld) :
|
|
spawnLocation);
|
|
}
|
|
|
|
if (!this.isAllowedOnServer(environment))
|
|
{
|
|
// Custom portal handling.
|
|
event.setCancelled(true);
|
|
|
|
// Teleport to standard nether or end
|
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
|
entity(event.getEntity()).
|
|
location(event.getTo()).
|
|
portal().
|
|
build();
|
|
}
|
|
}
|
|
}
|