2022-09-30 00:26:31 +02:00
|
|
|
//
|
|
|
|
// Created by BONNe
|
|
|
|
// Copyright - 2022
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
package world.bentobox.bentobox.listeners.teleports;
|
|
|
|
|
|
|
|
|
2022-12-29 19:01:25 +01:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Map;
|
2023-01-01 01:41:17 +01:00
|
|
|
import java.util.Objects;
|
2022-12-29 19:01:25 +01:00
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.UUID;
|
|
|
|
|
2022-09-30 00:26:31 +02:00
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Location;
|
|
|
|
import org.bukkit.Material;
|
|
|
|
import org.bukkit.World;
|
2022-09-30 01:25:44 +02:00
|
|
|
import org.bukkit.entity.Player;
|
2022-09-30 00:26:31 +02:00
|
|
|
import org.eclipse.jdt.annotation.NonNull;
|
2022-09-30 01:25:44 +02:00
|
|
|
import org.eclipse.jdt.annotation.Nullable;
|
2022-09-30 00:26:31 +02:00
|
|
|
|
|
|
|
import world.bentobox.bentobox.BentoBox;
|
|
|
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
|
|
|
import world.bentobox.bentobox.database.objects.Island;
|
2022-09-30 13:34:28 +02:00
|
|
|
import world.bentobox.bentobox.util.Util;
|
2022-09-30 00:26:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This abstract class contains all common methods for entity and player teleportation.
|
|
|
|
*/
|
|
|
|
public abstract class AbstractTeleportListener
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Instance of Teleportation processor.
|
|
|
|
* @param bentoBox BentoBox plugin.
|
|
|
|
*/
|
|
|
|
AbstractTeleportListener(@NonNull BentoBox bentoBox)
|
|
|
|
{
|
|
|
|
this.plugin = bentoBox;
|
|
|
|
this.inPortal = new HashSet<>();
|
|
|
|
this.inTeleport = new HashSet<>();
|
2022-09-30 01:25:44 +02:00
|
|
|
this.teleportOrigin = new HashMap<>();
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-01 01:41:17 +01:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Section: Methods
|
|
|
|
// ---------------------------------------------------------------------
|
2022-09-30 00:26:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get island at the given location
|
|
|
|
* @return optional island at given location
|
|
|
|
*/
|
|
|
|
protected Optional<Island> getIsland(Location location)
|
|
|
|
{
|
2022-09-30 01:25:44 +02:00
|
|
|
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()));
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if vanilla portals should be used
|
|
|
|
*
|
|
|
|
* @param world - game mode world
|
|
|
|
* @param environment - environment
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
protected boolean isMakePortals(World world, World.Environment environment)
|
|
|
|
{
|
|
|
|
return this.plugin.getIWM().getAddon(world).
|
2023-01-01 01:41:17 +01:00
|
|
|
map(gameMode -> this.isMakePortals(gameMode, environment)).
|
|
|
|
orElse(false);
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if vanilla portals should be used
|
|
|
|
*
|
|
|
|
* @param gameMode - game mode
|
|
|
|
* @param environment - environment
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
protected boolean isMakePortals(GameModeAddon gameMode, World.Environment environment)
|
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> gameMode.getWorldSettings().isMakeNetherPortals();
|
|
|
|
case THE_END -> gameMode.getWorldSettings().isMakeEndPortals();
|
|
|
|
default -> false;
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if nether or end are generated
|
|
|
|
*
|
|
|
|
* @param overWorld - game world
|
2022-09-30 13:34:28 +02:00
|
|
|
* @param environment - environment
|
2022-09-30 00:26:31 +02:00
|
|
|
* @return true or false
|
|
|
|
*/
|
2022-09-30 13:34:28 +02:00
|
|
|
protected boolean isAllowedInConfig(World overWorld, World.Environment environment)
|
2022-09-30 00:26:31 +02:00
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> this.plugin.getIWM().isNetherGenerate(overWorld);
|
|
|
|
case THE_END -> this.plugin.getIWM().isEndGenerate(overWorld);
|
|
|
|
default -> true;
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the default nether or end are allowed by the server settings
|
|
|
|
*
|
|
|
|
* @param environment - environment
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
protected boolean isAllowedOnServer(World.Environment environment)
|
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> Bukkit.getAllowNether();
|
|
|
|
case THE_END -> Bukkit.getAllowEnd();
|
|
|
|
default -> true;
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if nether or end islands are generated
|
|
|
|
*
|
|
|
|
* @param overWorld - over world
|
|
|
|
* @param environment - environment
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
protected boolean isIslandWorld(World overWorld, World.Environment environment)
|
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> this.plugin.getIWM().isNetherIslands(overWorld);
|
|
|
|
case THE_END -> this.plugin.getIWM().isEndIslands(overWorld);
|
|
|
|
default -> true;
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the nether or end world
|
|
|
|
*
|
|
|
|
* @param overWorld - over world
|
|
|
|
* @param environment - environment
|
|
|
|
* @return nether or end world
|
|
|
|
*/
|
|
|
|
protected World getNetherEndWorld(World overWorld, World.Environment environment)
|
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> this.plugin.getIWM().getNetherWorld(overWorld);
|
|
|
|
case THE_END -> this.plugin.getIWM().getEndWorld(overWorld);
|
|
|
|
default -> Util.getWorld(overWorld);
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the island has a nether or end island already
|
|
|
|
*
|
|
|
|
* @param island - island
|
|
|
|
* @param environment - environment
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
protected boolean hasPartnerIsland(Island island, World.Environment environment)
|
|
|
|
{
|
2022-09-30 13:34:28 +02:00
|
|
|
return switch (environment) {
|
2023-01-01 01:41:17 +01:00
|
|
|
case NETHER -> island.hasNetherIsland();
|
|
|
|
case THE_END -> island.hasEndIsland();
|
|
|
|
default -> true;
|
2022-09-30 13:34:28 +02:00
|
|
|
};
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method calculates the maximal search area for portal.
|
|
|
|
* @param location Location from which search should happen.
|
|
|
|
* @param island Island that contains the search point.
|
|
|
|
* @return Search range for portal.
|
|
|
|
*/
|
|
|
|
protected int calculateSearchRadius(Location location, Island island)
|
|
|
|
{
|
|
|
|
int diff;
|
|
|
|
|
|
|
|
if (island.onIsland(location))
|
|
|
|
{
|
|
|
|
// Find max x or max z
|
|
|
|
int x = Math.abs(island.getProtectionCenter().getBlockX() - location.getBlockX());
|
|
|
|
int z = Math.abs(island.getProtectionCenter().getBlockZ() - location.getBlockZ());
|
|
|
|
|
|
|
|
diff = Math.min(this.plugin.getSettings().getSafeSpotSearchRange(),
|
2023-01-01 01:41:17 +01:00
|
|
|
island.getProtectionRange() - Math.max(x, z));
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
diff = this.plugin.getSettings().getSafeSpotSearchRange();
|
|
|
|
}
|
|
|
|
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method calculates location for portal.
|
|
|
|
* @param fromLocation Location from which teleportation happens.
|
|
|
|
* @param fromWorld World from which teleportation happens.
|
|
|
|
* @param toWorld The target world.
|
|
|
|
* @param environment Portal variant.
|
|
|
|
* @param canCreatePortal Indicates if portal should be created or not.
|
|
|
|
* @return Location for new portal.
|
|
|
|
*/
|
|
|
|
protected Location calculateLocation(Location fromLocation,
|
2023-01-01 01:41:17 +01:00
|
|
|
World fromWorld,
|
|
|
|
World toWorld,
|
|
|
|
World.Environment environment,
|
|
|
|
boolean canCreatePortal)
|
2022-09-30 00:26:31 +02:00
|
|
|
{
|
|
|
|
// Null check - not that useful
|
|
|
|
if (fromWorld == null || toWorld == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Location toLocation = fromLocation.toVector().toLocation(toWorld);
|
|
|
|
|
|
|
|
if (!this.isMakePortals(fromWorld, environment))
|
|
|
|
{
|
2023-01-01 01:41:17 +01:00
|
|
|
toLocation = Objects.requireNonNullElse(this.getIsland(fromLocation).
|
|
|
|
map(island -> island.getSpawnPoint(toWorld.getEnvironment())).
|
|
|
|
orElse(toLocation), toLocation);
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Limit Y to the min/max world height.
|
|
|
|
toLocation.setY(Math.max(Math.min(toLocation.getY(), toWorld.getMaxHeight()), toWorld.getMinHeight()));
|
|
|
|
|
|
|
|
if (!canCreatePortal)
|
|
|
|
{
|
|
|
|
// Legacy portaling
|
|
|
|
return toLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make portals
|
|
|
|
// For anywhere other than the end - it is the player's location that is used
|
|
|
|
if (!environment.equals(World.Environment.THE_END))
|
|
|
|
{
|
|
|
|
return toLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the-end then we want the platform to always be generated in the same place no matter where
|
|
|
|
// they enter the portal
|
|
|
|
final int x = fromLocation.getBlockX();
|
|
|
|
final int z = fromLocation.getBlockZ();
|
|
|
|
final int y = fromLocation.getBlockY();
|
|
|
|
int i = x;
|
|
|
|
int j = z;
|
|
|
|
int k = y;
|
|
|
|
|
|
|
|
// If the from is not a portal, then we have to find it
|
|
|
|
if (!fromLocation.getBlock().getType().equals(Material.END_PORTAL))
|
|
|
|
{
|
2023-11-28 17:55:42 +01:00
|
|
|
// Search portal block 5 blocks in all directions from starting location. Return the first one.
|
|
|
|
boolean continueSearch = true;
|
|
|
|
|
|
|
|
// simplistic search pattern to look at all blocks from the middle outwards by preferring
|
|
|
|
// Y location first, then Z and as last X
|
|
|
|
// Proper implementation would require queue and distance calculation.
|
|
|
|
|
|
|
|
for (int offsetX = 0; continueSearch && offsetX < 10; offsetX++)
|
|
|
|
{
|
|
|
|
// Change sign based on mod value.
|
|
|
|
int posX = x + ((offsetX % 2 == 0) ? 1 : -1) * (offsetX / 2);
|
|
|
|
|
|
|
|
for (int offsetZ = 0; continueSearch && offsetZ < 10; offsetZ++)
|
|
|
|
{
|
|
|
|
// Change sign based on mod value.
|
|
|
|
int posZ = z + ((offsetZ % 2 == 0) ? 1 : -1) * (offsetZ / 2);
|
|
|
|
|
|
|
|
for (int offsetY = 0; continueSearch && offsetY < 10; offsetY++)
|
|
|
|
{
|
|
|
|
// Change sign based on mod value.
|
|
|
|
int posY = y + ((offsetY % 2 == 0) ? 1 : -1) * (offsetY / 2);
|
|
|
|
|
|
|
|
if (fromWorld.getBlockAt(posX, posY, posZ).getType().equals(Material.END_PORTAL))
|
|
|
|
{
|
|
|
|
i = posX;
|
|
|
|
j = posZ;
|
|
|
|
k = posY;
|
|
|
|
continueSearch = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the maximum x and z corner
|
|
|
|
for (; (i < x + 5) && fromWorld.getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++) ;
|
|
|
|
for (; (j < z + 5) && fromWorld.getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++) ;
|
|
|
|
|
|
|
|
// Mojang end platform generation is:
|
|
|
|
// AIR
|
|
|
|
// AIR
|
|
|
|
// OBSIDIAN
|
|
|
|
// and player is placed on second air block above obsidian.
|
|
|
|
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
|
2023-11-28 17:55:42 +01:00
|
|
|
return new Location(toWorld, i, Math.min(toWorld.getMaxHeight() - 2, Math.max(toWorld.getMinHeight() + 2, k)), j);
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-30 01:25:44 +02:00
|
|
|
/**
|
|
|
|
* 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 ->
|
2023-01-01 01:41:17 +01:00
|
|
|
island.getSpawnPoint(World.Environment.NORMAL) == null ?
|
2022-09-30 01:25:44 +02:00
|
|
|
island.getCenter() :
|
2023-01-01 01:41:17 +01:00
|
|
|
island.getSpawnPoint(World.Environment.NORMAL)).
|
|
|
|
orElse(this.plugin.getIslands().isSafeLocation(world.getSpawnLocation()) ?
|
|
|
|
world.getSpawnLocation() : null);
|
2022-09-30 01:25:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-30 00:26:31 +02:00
|
|
|
/**
|
2023-10-29 05:16:04 +01:00
|
|
|
* This method returns if missing islands should be generated upon teleportation.
|
2022-09-30 00:26:31 +02:00
|
|
|
* Can happen only in non-custom generators.
|
|
|
|
* @param overWorld OverWorld
|
|
|
|
* @return {@code true} if missing islands must be pasted, {@code false} otherwise.
|
|
|
|
*/
|
|
|
|
protected boolean isPastingMissingIslands(World overWorld)
|
|
|
|
{
|
|
|
|
return this.plugin.getIWM().isPasteMissingIslands(overWorld) &&
|
2023-01-01 01:41:17 +01:00
|
|
|
!this.plugin.getIWM().isUseOwnGenerator(overWorld);
|
2022-09-30 00:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-01 01:41:17 +01:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Section: Variables
|
|
|
|
// ---------------------------------------------------------------------
|
2022-09-30 00:26:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* BentoBox plugin instance.
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
protected final BentoBox plugin;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set of entities that currently is inside portal.
|
|
|
|
*/
|
|
|
|
protected final Set<UUID> inPortal;
|
|
|
|
|
2022-09-30 01:25:44 +02:00
|
|
|
/**
|
|
|
|
* Map that links entities origin of teleportation. Used for respawning.
|
|
|
|
*/
|
|
|
|
protected final Map<UUID, World> teleportOrigin;
|
|
|
|
|
2022-09-30 00:26:31 +02:00
|
|
|
/**
|
|
|
|
* Set of entities that currently is in teleportation.
|
|
|
|
*/
|
|
|
|
protected final Set<UUID> inTeleport;
|
|
|
|
}
|