Rewrote SafeSpotTeleport

Moved coords to longs instead of ints for island locations

Created a SafeTeleportBuilder class

Created a utility Pair class
This commit is contained in:
Tastybento 2018-02-10 13:09:32 -08:00
parent 4bd02a59ef
commit ed65aa421d
9 changed files with 308 additions and 206 deletions

View File

@ -8,7 +8,6 @@ import org.bukkit.Location;
import us.tastybento.bskyblock.Constants; import us.tastybento.bskyblock.Constants;
import us.tastybento.bskyblock.api.commands.CompositeCommand; import us.tastybento.bskyblock.api.commands.CompositeCommand;
import us.tastybento.bskyblock.api.commands.User; import us.tastybento.bskyblock.api.commands.User;
import us.tastybento.bskyblock.util.SafeSpotTeleport;
import us.tastybento.bskyblock.util.SafeTeleportBuilder; import us.tastybento.bskyblock.util.SafeTeleportBuilder;
public class AdminTeleportCommand extends CompositeCommand { public class AdminTeleportCommand extends CompositeCommand {

View File

@ -30,7 +30,7 @@ public class IslandCache {
*/ */
private HashMap<UUID, Island> islandsByUUID; private HashMap<UUID, Island> islandsByUUID;
// 2D islandGrid of islands, x,z // 2D islandGrid of islands, x,z
private TreeMap<Integer, TreeMap<Integer, Island>> islandGrid = new TreeMap<>(); private TreeMap<Long, TreeMap<Long, Island>> islandGrid = new TreeMap<>();
public IslandCache() { public IslandCache() {
islandsByLocation = HashBiMap.create(); islandsByLocation = HashBiMap.create();
@ -72,7 +72,7 @@ public class IslandCache {
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: min x is in the grid :" + newIsland.getMinX()); plugin.getLogger().info("DEBUG: min x is in the grid :" + newIsland.getMinX());
} }
TreeMap<Integer, Island> zEntry = islandGrid.get(newIsland.getMinX()); TreeMap<Long, Island> zEntry = islandGrid.get(newIsland.getMinX());
if (zEntry.containsKey(newIsland.getMinZ())) { if (zEntry.containsKey(newIsland.getMinZ())) {
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: min z is in the grid :" + newIsland.getMinZ()); plugin.getLogger().info("DEBUG: min z is in the grid :" + newIsland.getMinZ());
@ -111,7 +111,7 @@ public class IslandCache {
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ()); plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ());
} }
TreeMap<Integer, Island> zEntry = new TreeMap<>(); TreeMap<Long, Island> zEntry = new TreeMap<>();
zEntry.put(newIsland.getMinZ(), newIsland); zEntry.put(newIsland.getMinZ(), newIsland);
islandGrid.put(newIsland.getMinX(), zEntry); islandGrid.put(newIsland.getMinX(), zEntry);
} }
@ -177,8 +177,8 @@ public class IslandCache {
plugin.getLogger().info("DEBUG: deleting island at " + island.getCenter()); plugin.getLogger().info("DEBUG: deleting island at " + island.getCenter());
} }
if (island != null) { if (island != null) {
int x = island.getMinX(); long x = island.getMinX();
int z = island.getMinZ(); long z = island.getMinZ();
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: x = " + x + " z = " + z); plugin.getLogger().info("DEBUG: x = " + x + " z = " + z);
} }
@ -186,7 +186,7 @@ public class IslandCache {
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: x found"); plugin.getLogger().info("DEBUG: x found");
} }
TreeMap<Integer, Island> zEntry = islandGrid.get(x); TreeMap<Long, Island> zEntry = islandGrid.get(x);
if (zEntry.containsKey(z)) { if (zEntry.containsKey(z)) {
if (DEBUG) { if (DEBUG) {
plugin.getLogger().info("DEBUG: z found - deleting the island"); plugin.getLogger().info("DEBUG: z found - deleting the island");
@ -228,14 +228,14 @@ public class IslandCache {
* @param z * @param z
* @return Island or null * @return Island or null
*/ */
public Island getIslandAt(int x, int z) { public Island getIslandAt(long x, long z) {
if (DEBUG2) { if (DEBUG2) {
plugin.getLogger().info("DEBUG: getting island at " + x + "," + z); plugin.getLogger().info("DEBUG: getting island at " + x + "," + z);
plugin.getLogger().info("DEBUG: island grid is " + islandGrid.size()); plugin.getLogger().info("DEBUG: island grid is " + islandGrid.size());
} }
Entry<Integer, TreeMap<Integer, Island>> en = islandGrid.floorEntry(x); Entry<Long, TreeMap<Long, Island>> en = islandGrid.floorEntry(x);
if (en != null) { if (en != null) {
Entry<Integer, Island> ent = en.getValue().floorEntry(z); Entry<Long, Island> ent = en.getValue().floorEntry(z);
if (ent != null) { if (ent != null) {
// Check if in the island range // Check if in the island range
Island island = ent.getValue(); Island island = ent.getValue();

View File

@ -626,8 +626,8 @@ public class IslandsManager {
} }
// Try to fix this teleport location and teleport the player if possible // Try to fix this teleport location and teleport the player if possible
new SafeTeleportBuilder(plugin).entity(player) new SafeTeleportBuilder(plugin).entity(player)
.location(plugin.getPlayers().getHomeLocation(player.getUniqueId(), number)) .island(plugin.getIslands().getIsland(player.getUniqueId()))
.setHome(true) .portal(false)
.homeNumber(number) .homeNumber(number)
.build(); .build();
return; return;

View File

@ -25,6 +25,7 @@ import us.tastybento.bskyblock.api.flags.Flag;
import us.tastybento.bskyblock.database.objects.adapters.Adapter; import us.tastybento.bskyblock.database.objects.adapters.Adapter;
import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer; import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer;
import us.tastybento.bskyblock.managers.RanksManager; import us.tastybento.bskyblock.managers.RanksManager;
import us.tastybento.bskyblock.util.Pair;
import us.tastybento.bskyblock.util.Util; import us.tastybento.bskyblock.util.Util;
/** /**
@ -47,14 +48,14 @@ public class Island implements DataObject {
private int range; private int range;
// Coordinates of the island area // Coordinates of the island area
private int minX; private long minX;
private int minZ; private long minZ;
// Coordinates of minimum protected area // Coordinates of minimum protected area
private int minProtectedX; private long minProtectedX;
private int minProtectedZ; private long minProtectedZ;
// Protection size // Protection size
private int protectionRange; private int protectionRange;
@ -212,28 +213,28 @@ public class Island implements DataObject {
/** /**
* @return the minProtectedX * @return the minProtectedX
*/ */
public int getMinProtectedX() { public long getMinProtectedX() {
return minProtectedX; return minProtectedX;
} }
/** /**
* @return the minProtectedZ * @return the minProtectedZ
*/ */
public int getMinProtectedZ() { public long getMinProtectedZ() {
return minProtectedZ; return minProtectedZ;
} }
/** /**
* @return the minX * @return the minX
*/ */
public int getMinX() { public long getMinX() {
return minX; return minX;
} }
/** /**
* @return the minZ * @return the minZ
*/ */
public int getMinZ() { public long getMinZ() {
return minZ; return minZ;
} }
@ -315,8 +316,8 @@ public class Island implements DataObject {
*/ */
public int getTileEntityCount(Material material, World world) { public int getTileEntityCount(Material material, World world) {
int result = 0; int result = 0;
for (int x = getMinProtectedX() /16; x <= (getMinProtectedX() + getProtectionRange() - 1)/16; x++) { for (int x = (int) (getMinProtectedX() /16); x <= (getMinProtectedX() + getProtectionRange() - 1)/16; x++) {
for (int z = getMinProtectedZ() /16; z <= (getMinProtectedZ() + getProtectionRange() - 1)/16; z++) { for (int z = (int) (getMinProtectedZ() /16); z <= (getMinProtectedZ() + getProtectionRange() - 1)/16; z++) {
for (BlockState holder : world.getChunkAt(x, z).getTileEntities()) { for (BlockState holder : world.getChunkAt(x, z).getTileEntities()) {
//plugin.getLogger().info("DEBUG: tile entity: " + holder.getType()); //plugin.getLogger().info("DEBUG: tile entity: " + holder.getType());
if (onIsland(holder.getLocation())) { if (onIsland(holder.getLocation())) {
@ -396,22 +397,31 @@ public class Island implements DataObject {
return center.getBlockZ(); return center.getBlockZ();
} }
public boolean inIslandSpace(Location location) {
if (Util.inWorld(location)) {
return inIslandSpace(location.getBlockX(), location.getBlockZ());
}
return false;
}
/** /**
* Checks if coords are in the island space * Checks if coords are in the island space
* @param x * @param x
* @param z * @param z
* @return true if in the island space * @return true if in the island space
*/ */
public boolean inIslandSpace(int x, int z) { public boolean inIslandSpace(long x, long z) {
//Bukkit.getLogger().info("DEBUG: center - " + center); //Bukkit.getLogger().info("DEBUG: center - " + center);
return x >= minX && x < minX + range*2 && z >= minZ && z < minZ + range*2; return x >= minX && x < minX + range*2 && z >= minZ && z < minZ + range*2;
} }
public boolean inIslandSpace(Location location) { /**
if (Util.inWorld(location)) { * Checks if the coords are in island space
return inIslandSpace(location.getBlockX(), location.getBlockZ()); * @param blockCoord
} * @return true or false
return false; */
public boolean inIslandSpace(Pair<Long, Long> blockCoord) {
return inIslandSpace(blockCoord.x, blockCoord.z);
} }
/** /**
@ -567,28 +577,29 @@ public class Island implements DataObject {
/** /**
* @param minProtectedX the minProtectedX to set * @param minProtectedX the minProtectedX to set
*/ */
public void setMinProtectedX(int minProtectedX) { public final void setMinProtectedX(long minProtectedX) {
this.minProtectedX = minProtectedX; this.minProtectedX = minProtectedX;
} }
/** /**
* @param minProtectedZ the minProtectedZ to set * @param minProtectedZ the minProtectedZ to set
*/ */
public void setMinProtectedZ(int minProtectedZ) { public final void setMinProtectedZ(long minProtectedZ) {
this.minProtectedZ = minProtectedZ; this.minProtectedZ = minProtectedZ;
} }
/** /**
* @param minX the minX to set * @param minX the minX to set
*/ */
public void setMinX(int minX) { public final void setMinX(long minX) {
this.minX = minX; this.minX = minX;
} }
/** /**
* @param minZ the minZ to set * @param minZ the minZ to set
*/ */
public void setMinZ(int minZ) { public final void setMinZ(long minZ) {
this.minZ = minZ; this.minZ = minZ;
} }

View File

@ -16,9 +16,9 @@ public class IslandWorld {
private static final String CREATING = "Creating "; private static final String CREATING = "Creating ";
private BSkyBlock plugin; private BSkyBlock plugin;
private static World islandWorld; private World islandWorld;
private static World netherWorld; private World netherWorld;
private static World endWorld; private World endWorld;
/** /**
* Generates the Skyblock worlds. * Generates the Skyblock worlds.

View File

@ -34,10 +34,10 @@ public class DeleteIslandChunks {
if (world == null) { if (world == null) {
return; return;
} }
int minXChunk = island.getMinX() / 16; int minXChunk = (int) (island.getMinX() / 16);
int maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) /16; int maxXChunk = (int) ((island.getRange() * 2 + island.getMinX() - 1) /16);
int minZChunk = island.getMinZ() / 16; int minZChunk = (int) (island.getMinZ() / 16);
int maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) /16; int maxZChunk = (int) ((island.getRange() * 2 + island.getMinZ() - 1) /16);
for (int x = minXChunk; x <= maxXChunk; x++) { for (int x = minXChunk; x <= maxXChunk; x++) {
for (int z = minZChunk; z<=maxZChunk; z++) { for (int z = minZChunk; z<=maxZChunk; z++) {
world.regenerateChunk(x, z); world.regenerateChunk(x, z);

View File

@ -0,0 +1,65 @@
package us.tastybento.bskyblock.util;
public class Pair<X, Z> {
public X x;
public Z z;
public Pair(X x, Z z) {
this.x = x;
this.z = z;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Pair [x=" + x + ", z=" + z + "]";
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((x == null) ? 0 : x.hashCode());
result = prime * result + ((z == null) ? 0 : z.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Pair)) {
return false;
}
Pair<?, ?> other = (Pair<?, ?>) obj;
if (x == null) {
if (other.x != null) {
return false;
}
} else if (!x.equals(other.x)) {
return false;
}
if (z == null) {
if (other.z != null) {
return false;
}
} else if (!z.equals(other.z)) {
return false;
}
return true;
}
}

View File

@ -1,21 +1,22 @@
package us.tastybento.bskyblock.util; package us.tastybento.bskyblock.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot; import org.bukkit.ChunkSnapshot;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.commands.User;
import us.tastybento.bskyblock.database.objects.Island; import us.tastybento.bskyblock.database.objects.Island;
/** /**
@ -26,197 +27,221 @@ import us.tastybento.bskyblock.database.objects.Island;
public class SafeSpotTeleport { public class SafeSpotTeleport {
private enum State { private enum State {
CENTER, SURROUNDING, LAST_CHECK, FAILURE, CENTER_WAIT, SURROUNDING_WAIT CHECKING, WAITING
} }
private static final int MAX_CHUNKS = 10;
private static final long SPEED = 10; private static final long SPEED = 10;
private State step = State.CENTER; private State step = State.CHECKING;
private BukkitTask task; private BukkitTask task;
// Parameters
private final Entity entity;
private final Location location;
private final boolean portal;
private final int homeNumber;
// Locations
private Location bestSpot;
private BSkyBlock plugin; private BSkyBlock plugin;
private final Entity entity; private List<Pair<Integer, Integer>> chunksToScan;
private final Location location;
private final int homeNumber;
private final boolean setHome;
private int lastX;
private int lastZ;
private int chunksToCheck = 10;
private int worldHeight = 255;
private World world;
private double safeDistance;
private Vector safeSpotInChunk;
private boolean safeSpotFound;
private Vector portalPart;
private ChunkSnapshot portalChunk;
private ChunkSnapshot safeChunk;
/** /**
* Teleports and entity to a safe spot on island * Teleports and entity to a safe spot on island
* @param plugin2 * @param plugin
* @param entity2 * @param entity
* @param island * @param location
* @param failureMessage * @param failureMessage
* @param setHome2 * @param portal
* @param homeNumber2 * @param homeNumber
*/ */
public SafeSpotTeleport(BSkyBlock plugin2, Entity entity2, Location location, String failureMessage, boolean setHome2, public SafeSpotTeleport(BSkyBlock plugin, Entity entity, Location location, String failureMessage, boolean portal,
int homeNumber2) { int homeNumber) {
this.plugin = plugin2; this.plugin = plugin;
this.entity = entity2; this.entity = entity;
this.setHome = setHome2;
this.homeNumber = homeNumber2;
this.location = location; this.location = location;
this.portal = portal;
this.homeNumber = homeNumber;
// Put player into spectator mode // Put player into spectator mode
if (entity instanceof Player && ((Player)entity).getGameMode().equals(GameMode.SURVIVAL)) { if (entity instanceof Player && ((Player)entity).getGameMode().equals(GameMode.SURVIVAL)) {
((Player)entity).setGameMode(GameMode.SPECTATOR); ((Player)entity).setGameMode(GameMode.SPECTATOR);
} }
// Get world info
world = location.getWorld();
worldHeight = world.getEnvironment().equals(Environment.NETHER) ? world.getMaxHeight() - 20 : world.getMaxHeight() - 2;
// Get island mins and max // Get chunks to scan
Island island = plugin.getIslands().getIslandAt(location).orElse(null); chunksToScan = getChunksToScan();
if (island == null) {
if (entity instanceof Player) {
User.getInstance((Player)entity).sendMessage(failureMessage);
}
return;
}
// Set the minimums and maximums
lastX = island.getMinProtectedX() / 16;
lastZ = island.getMinProtectedZ() / 16;
int biggestX = (island.getMinProtectedX() + island.getProtectionRange() - 1) / 16;
int biggestZ = (island.getMinProtectedZ() + island.getProtectionRange() - 1) / 16;
// Start a recurring task until done or cancelled // Start a recurring task until done or cancelled
task = plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { task = plugin.getServer().getScheduler().runTaskTimer(plugin, () -> {
Bukkit.getLogger().info("State = " + step);
Bukkit.getLogger().info("Chunks to scan size = " + chunksToScan.size());
List<ChunkSnapshot> chunkSnapshot = new ArrayList<>(); List<ChunkSnapshot> chunkSnapshot = new ArrayList<>();
switch (step) { switch (step) {
case CENTER: case CHECKING:
// Add the center chunk Iterator<Pair<Integer, Integer>> it = chunksToScan.iterator();
chunkSnapshot.add(location.toVector().toLocation(world).getChunk().getChunkSnapshot()); if (!it.hasNext()) {
// Add immediately adjacent chunks Bukkit.getLogger().info("Nothing left!");
for (int x = location.getChunk().getX()-1; x <= location.getChunk().getX()+1; x++) { // Nothing left
for (int z = location.getChunk().getZ()-1; z <= location.getChunk().getZ()+1; z++) { tidyUp(entity, failureMessage);
if (x != location.getChunk().getX() || z != location.getChunk().getZ()) { return;
chunkSnapshot.add(world.getChunkAt(x, z).getChunkSnapshot()); }
} // Add chunk snapshots to the list
} while (it.hasNext() && chunkSnapshot.size() < MAX_CHUNKS) {
Pair<Integer, Integer> pair = it.next();
chunkSnapshot.add(location.getWorld().getChunkAt(pair.x, pair.z).getChunkSnapshot());
it.remove();
} }
// Move to next step // Move to next step
step = State.CENTER_WAIT; step = State.WAITING;
Bukkit.getLogger().info("Chunk snapshot size = " + chunkSnapshot.size());
checkChunks(chunkSnapshot); checkChunks(chunkSnapshot);
break; break;
case CENTER_WAIT: case WAITING:
// Do nothing while the center scan is done // Do nothing while the scan is done
break; break;
case SURROUNDING:
for (int x = lastX; x <= biggestX; x++) {
for (int z = lastZ; z <= biggestZ; z++) {
chunkSnapshot.add(world.getChunkAt(x, z).getChunkSnapshot());
if (chunkSnapshot.size() == chunksToCheck) {
lastX = x;
lastZ = z;
step = State.SURROUNDING_WAIT;
checkChunks(chunkSnapshot);
return;
}
}
}
// Last few chunks, may be none
step = State.LAST_CHECK;
checkChunks(chunkSnapshot);
break;
case SURROUNDING_WAIT:
// Do nothing while the surrounding scan is done
break;
case LAST_CHECK:
// Do nothing while the last few chunks are scanned
break;
case FAILURE:
// We are done searching - failure
task.cancel();
if (entity instanceof Player) {
if (!failureMessage.isEmpty()) {
entity.sendMessage(failureMessage);
}
}
} }
}, 0L, SPEED); }, 0L, SPEED);
} }
private boolean checkChunks(List<ChunkSnapshot> chunkSnapshot) { private void tidyUp(Entity entity, String failureMessage) {
// Nothing left to check and still not canceled
task.cancel();
// Check portal
if (portal && bestSpot != null) {
Bukkit.getLogger().info("No portals found, going to best spot");
// No portals found, teleport to the best spot we found
teleportEntity(bestSpot);
}
// Failed - no safe spot
if (entity instanceof Player && !failureMessage.isEmpty()) {
entity.sendMessage(failureMessage);
}
}
/**
* Gets a set of chunk coords that will be scanned.
* @param entity
* @param location
* @return
*/
private List<Pair<Integer, Integer>> getChunksToScan() {
List<Pair<Integer, Integer>> result = new ArrayList<>();
// Get island if available
Optional<Island> island = plugin.getIslands().getIslandAt(location);
int maxRadius = island.map(x -> x.getProtectionRange()).orElse(plugin.getSettings().getIslandProtectionRange());
Bukkit.getLogger().info("default island radius = " + plugin.getSettings().getIslandProtectionRange());
Bukkit.getLogger().info("Max radius = " + maxRadius);
int x = location.getBlockX();
int z = location.getBlockZ();
// Create ever increasing squares around the target location
int radius = 0;
do {
for (long i = x - radius; i <= x + radius; i++) {
for (long j = z - radius; j <= z + radius; j++) {
Pair<Long, Long> blockCoord = new Pair<>(i,j);
Pair<Integer, Integer> chunkCoord = new Pair<>((int)i/16, (int)j/16);
if (!result.contains(chunkCoord)) {
Bukkit.getLogger().info("Block coord = " + blockCoord);
Bukkit.getLogger().info("New chunk coord " + chunkCoord);
// Add the chunk coord
if (!island.isPresent()) {
// If there is no island, just add it
Bukkit.getLogger().info("No island, adding chunk coord ");
result.add(chunkCoord);
} else {
// If there is an island, only add it if the coord is in island space
island.ifPresent(is -> {
if (is.inIslandSpace(blockCoord)) {
Bukkit.getLogger().info("Island, adding chunk coord");
result.add(chunkCoord);
} else {
Bukkit.getLogger().info("Island, block coord not in island space");
}
});
}
}
}
}
radius++;
} while (radius < maxRadius);
return result;
}
/**
* Loops through the chunks and if a safe spot is found, fires off the teleportation
* @param chunkSnapshot
*/
private void checkChunks(List<ChunkSnapshot> chunkSnapshot) {
// Run async task to scan chunks
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
// Find a safe spot, defined as a solid block, with 2 air spaces above it
//long time = System.nanoTime();
int x = 0;
int y = 0;
int z = 0;
double distance = 0D;
for (ChunkSnapshot chunk: chunkSnapshot) { for (ChunkSnapshot chunk: chunkSnapshot) {
// Run through the chunk if (scanChunk(chunk)) {
for (x = 0; x< 16; x++) { return;
for (z = 0; z < 16; z++) {
// Work down from the entry point up
for (y = Math.min(chunk.getHighestBlockYAt(x, z), worldHeight); y >= 0; y--) {
//System.out.println("Trying " + (16 * chunk.getX() + x) + " " + y + " " + (16 * chunk.getZ() + z));
// Check for portal - only if this is not a safe home search
if (!setHome && chunk.getBlockType(x, y, z).equals(Material.PORTAL)) {
if (portalPart == null || (distance > location.toVector().distanceSquared(new Vector(x,y,z)))) {
// First one found or a closer one, save the chunk the position and the distance
portalChunk = chunk;
portalPart = new Vector(x,y,z);
distance = portalPart.distanceSquared(location.toVector());
}
}
// Check for safe spot, but only if it is closer than one we have found already
if (!safeSpotFound || (safeDistance > location.toVector().distanceSquared(new Vector(x,y,z)))) {
// No safe spot yet, or closer distance
if (checkBlock(chunk,x,y,z, worldHeight)) {
safeChunk = chunk;
safeSpotFound = true;
safeSpotInChunk = new Vector(x,y,z);
safeDistance = location.toVector().distanceSquared(safeSpotInChunk);
}
}
}
} //end z
} // end x
// If this is not a home search do a check for portal
if (!this.setHome) {
checkPortal();
} }
// If successful, teleport otherwise move to the next step in the state machine
if (safeSpotFound) {
task.cancel();
teleportEntity();
} else if (step.equals(State.SURROUNDING_WAIT) || step.equals(State.CENTER_WAIT)) {
step = State.SURROUNDING;
} else if (step.equals(State.LAST_CHECK)) {
step = State.FAILURE;
}
} }
// Nothing happened, change state
step = State.CHECKING;
}); });
return false;
} }
/**
* @param chunk
* @return true if a safe spot was found
*/
private boolean scanChunk(ChunkSnapshot chunk) {
Bukkit.getLogger().info("Scanning chunk at " + chunk.getX() + " " + chunk.getZ());
Bukkit.getLogger().info("Portal = " + portal);
World world = location.getWorld();
// Max height
int maxHeight = location.getWorld().getMaxHeight() - 20;
// Run through the chunk
for (int x = 0; x< 16; x++) {
for (int z = 0; z < 16; z++) {
// Work down from the entry point up
for (int y = Math.min(chunk.getHighestBlockYAt(x, z), maxHeight); y >= 0; y--) {
if (checkBlock(chunk, x,y,z, maxHeight)) {
Bukkit.getLogger().info("safe: " + x + " " + y + " "+ z);
Vector newSpot = new Vector(chunk.getX() * 16 + x + 0.5D, y + 1, chunk.getZ() * 16 + z + 0.5D);
// Check for portal
if (portal) {
if (chunk.getBlockType(x, y, z).equals(Material.PORTAL)) {
// Teleport as soon as we find a portal
teleportEntity(newSpot.toLocation(world));
return true;
} else if (bestSpot == null ) {
// Stash the best spot
bestSpot = newSpot.toLocation(world);
}
} else {
// Regular search - teleport as soon as we find something
teleportEntity(newSpot.toLocation(world));
return true;
}
}
}
} //end z
} // end x
return false;
}
/** /**
* Teleports entity to the safe spot * Teleports entity to the safe spot
*/ */
private void teleportEntity() { private void teleportEntity(Location loc) {
final Vector spot = new Vector((16 *safeChunk.getX()) + 0.5D, 1, (16 * safeChunk.getZ()) + 0.5D).add(safeSpotInChunk); task.cancel();
// Return to main thread and teleport the player // Return to main thread and teleport the player
plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.getServer().getScheduler().runTask(plugin, () -> {
Location destination = spot.toLocation(world); if (!portal && entity instanceof Player) {
if (setHome && entity instanceof Player) { // Set home
plugin.getPlayers().setHomeLocation(entity.getUniqueId(), destination, homeNumber); plugin.getPlayers().setHomeLocation(entity.getUniqueId(), loc, homeNumber);
} }
Vector velocity = entity.getVelocity(); Vector velocity = entity.getVelocity();
entity.teleport(destination); entity.teleport(loc);
// Exit spectator mode if in it // Exit spectator mode if in it
if (entity instanceof Player) { if (entity instanceof Player) {
Player player = (Player)entity; Player player = (Player)entity;
@ -230,10 +255,12 @@ public class SafeSpotTeleport {
} }
/** /**
* Checks if a portal is safe * Subscan to find the bottom of the portal
*/ */
private void checkPortal() { /*
private Location checkPortal() {
if (portalPart == null) { if (portalPart == null) {
return; return;
} }
@ -254,7 +281,7 @@ public class SafeSpotTeleport {
safeSpotInChunk = new Vector(x,y,z); safeSpotInChunk = new Vector(x,y,z);
safeChunk = portalChunk; safeChunk = portalChunk;
} }
} }*/
/** /**
* Returns true if the location is a safe one. * Returns true if the location is a safe one.
@ -265,21 +292,21 @@ public class SafeSpotTeleport {
* @param worldHeight * @param worldHeight
* @return * @return
*/ */
@SuppressWarnings("deprecation")
private boolean checkBlock(ChunkSnapshot chunk, int x, int y, int z, int worldHeight) { private boolean checkBlock(ChunkSnapshot chunk, int x, int y, int z, int worldHeight) {
int type = chunk.getBlockTypeId(x, y, z); Bukkit.getLogger().info("checking " + x + " " + y + " "+ z);
if (type != 0) { // AIR Material type = chunk.getBlockType(x, y, z);
int space1 = chunk.getBlockTypeId(x, Math.min(y + 1, worldHeight), z); if (!type.equals(Material.AIR)) { // AIR
int space2 = chunk.getBlockTypeId(x, Math.min(y + 2, worldHeight), z); Material space1 = chunk.getBlockType(x, Math.min(y + 1, worldHeight), z);
if ((space1 == 0 && space2 == 0) || (space1 == Material.PORTAL.getId() || space2 == Material.PORTAL.getId())) { Material space2 = chunk.getBlockType(x, Math.min(y + 2, worldHeight), z);
if ((space1.equals(Material.AIR) && space2.equals(Material.AIR))
|| (space1.equals(Material.PORTAL) && space2.equals(Material.PORTAL))) {
// Now there is a chance that this is a safe spot // Now there is a chance that this is a safe spot
// Check for safe ground // Check for safe ground
Material mat = Material.getMaterial(type); if (!type.toString().contains("FENCE")
if (!mat.toString().contains("FENCE") && !type.toString().contains("DOOR")
&& !mat.toString().contains("DOOR") && !type.toString().contains("GATE")
&& !mat.toString().contains("GATE") && !type.toString().contains("PLATE")) {
&& !mat.toString().contains("PLATE")) { switch (type) {
switch (mat) {
// Unsafe // Unsafe
case ANVIL: case ANVIL:
case BARRIER: case BARRIER:
@ -307,11 +334,10 @@ public class SafeSpotTeleport {
case WATER: case WATER:
case WEB: case WEB:
case WOOD_BUTTON: case WOOD_BUTTON:
//System.out.println("Block is dangerous " + mat.toString()); //Block is dangerous
break; break;
default: default:
// Safe // Safe
// System.out.println("Block is safe " + mat.toString());
return true; return true;
} }
} }
@ -320,4 +346,5 @@ public class SafeSpotTeleport {
return false; return false;
} }
} }

View File

@ -11,7 +11,7 @@ public class SafeTeleportBuilder {
private BSkyBlock plugin; private BSkyBlock plugin;
private Entity entity; private Entity entity;
private int homeNumber = 0; private int homeNumber = 0;
private boolean setHome = false; private boolean portal = false;
private String failureMessage = ""; private String failureMessage = "";
private Location location; private Location location;
@ -51,12 +51,12 @@ public class SafeTeleportBuilder {
} }
/** /**
* Set the home of the player to the safe location * This is a portal teleportation
* @param setHome * @param setHome
* @return * @return
*/ */
public SafeTeleportBuilder setHome(boolean setHome) { public SafeTeleportBuilder portal(boolean portal) {
this.setHome = setHome; this.portal = portal;
return this; return this;
} }
@ -85,7 +85,7 @@ public class SafeTeleportBuilder {
* @return * @return
*/ */
public SafeSpotTeleport build() { public SafeSpotTeleport build() {
return new SafeSpotTeleport(plugin, entity, location, failureMessage, setHome, homeNumber); return new SafeSpotTeleport(plugin, entity, location, failureMessage, portal, homeNumber);
} }