Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add safe-teleport detection to spawn points. #7530

Merged
merged 10 commits into from
Sep 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,12 @@ public enum ConfigNodes {
"# +------------------------------------------------------+ #",
"############################################################",
""),
SPAWNING_SAFE_TELEPORT(
"spawning.safe_teleport",
"false",
"# If enabled tries to find a safe location when teleporting to a town spawn/nation spawn/outpost",
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
"# can be used to prevent players from making kill boxes at those locations."
),
SPAWNING_COST_SPAWN_WARNINGS(
"spawning.spawn_cost_warnings",
"true",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3663,6 +3663,10 @@ public static String getWebMapUrl() {
return getString(ConfigNodes.PLUGIN_WEB_MAP_URL);
}

public static boolean getSafetyTeleport() {
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
return getBoolean(ConfigNodes.SPAWNING_SAFE_TELEPORT);
}

public static Map<Integer, TownLevel> getConfigTownLevel() {
return configTownLevel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

Intybyte marked this conversation as resolved.
Show resolved Hide resolved
import com.palmergames.bukkit.towny.event.NationSpawnEvent;
import com.palmergames.bukkit.towny.event.SpawnEvent;
import com.palmergames.bukkit.towny.event.TownSpawnEvent;
Expand All @@ -17,9 +18,13 @@
import com.palmergames.bukkit.towny.object.spawnlevel.TownSpawnLevel;

import com.palmergames.bukkit.towny.tasks.TeleportWarmupTimerTask;
import com.palmergames.bukkit.util.ItemLists;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;

Expand Down Expand Up @@ -440,19 +445,88 @@ else if (town != null && town.hasSpawn())
return player.getWorld().getSpawnLocation();
});
} else if (town != null && town.hasSpawn())
yield CompletableFuture.completedFuture(town.getSpawnOrNull());
yield CompletableFuture.completedFuture(getSafeLocation(town.getSpawnOrNull()));
else
yield CompletableFuture.completedFuture(player.getWorld().getSpawnLocation());
case TOWN:
if (outpost)
yield CompletableFuture.completedFuture(getOutpostSpawnLocation(town, split));
yield CompletableFuture.completedFuture(getSafeLocation(getOutpostSpawnLocation(town, split)));
else
yield CompletableFuture.completedFuture(town.getSpawn());
yield CompletableFuture.completedFuture(getSafeLocation(town.getSpawn()));
case NATION:
yield CompletableFuture.completedFuture(nation.getSpawn());
yield CompletableFuture.completedFuture(getSafeLocation(nation.getSpawn()));
};
}

/**
* Tries to find a safe location nearby to teleport the player to
* if safety teleport is enabled, otherwise does nothing.
*
* @param location Starting location
* @return A safe location nearby, same location if already safe
*/
private static Location getSafeLocation(Location location) {
//if safety teleport isn't enabled do nothing
if (!TownySettings.getSafetyTeleport()) {
return location;
}

if (isSafeLocation(location)) {
return location;
}

Location up = location.clone();
Location down = location.clone();

// look for 20 blocks up and down for a safe location,
// if you can't find it fail the teleport
// maybe add a translation key and add the player
// as a parameter to print him an error message
for(int i = 0; i < 20; i++) {
up = up.add(0,1,0);
down = down.subtract(0, 1, 0);

if (isSafeLocation(up)) {
return up;
}

if (isSafeLocation(down)) {
return down;
}
}

return null;
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
}

private static boolean isSafeLocation(Location location) {
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
World world = location.getWorld();
if (world == null) return false;

// Check if location is in a block
Block block = world.getBlockAt(location);
Material type = block.getType();

if (!ItemLists.NOT_SOLID_BLOCKS.contains(type) || ItemLists.LIQUID_BLOCKS.contains(type)) {
return false;
}

// Check if block below is lava or water or nothing
Block belowBlock = world.getBlockAt(location.clone().subtract(0, 1, 0));
Material belowType = belowBlock.getType();
if (ItemLists.AIR_TYPES.contains(belowType) || ItemLists.LIQUID_BLOCKS.contains(type)) {
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

// Check if the location is directly above a solid block
Block aboveBlock = world.getBlockAt(location.clone().add(0, 1, 0));
Material aboveType = aboveBlock.getType();
if (!ItemLists.NOT_SOLID_BLOCKS.contains(aboveType) || ItemLists.LIQUID_BLOCKS.contains(aboveType)) {
return false;
}

return true;
}

/**
* Complicated code that parses the given split to a named, numbered or
* unnumbered outpost.
Expand Down
46 changes: 44 additions & 2 deletions Towny/src/main/java/com/palmergames/bukkit/util/ItemLists.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
LlmDl marked this conversation as resolved.
Show resolved Hide resolved
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -257,6 +256,11 @@ public Collection<String> getMaterialNameCollection() {
*/
public static final ItemLists CAULDRON_FILLABLE = newBuilder().add("WATER_BUCKET", "LAVA_BUCKET", "POWDER_SNOW_BUCKET").build();

/**
* List of liquid blocks
*/
public static final ItemLists LIQUID_BLOCKS = newBuilder().add("WATER", "LAVA", "BUBBLE_COLUMN").build();

/**
* List of hoes
*/
Expand All @@ -278,11 +282,49 @@ public Collection<String> getMaterialNameCollection() {
.includeList(PLANTABLES)
.add("TALL_GRASS","LARGE_FERN","VINE","TWISTING_VINES_PLANT","WEEPING_VINES_PLANT","NETHER_WART_BLOCK","CRIMSON_ROOTS","WARPED_ROOTS","NETHER_SPROUTS","BIG_DRIPLEAF","SMALL_DRIPLEAF").build();

/**
* Minecraft uses 3 types of air
*/
public static final ItemLists AIR_TYPES = newBuilder()
.add("AIR")
.add("CAVE_AIR")
.add("VOID_AIR").build();

/**
* List of all the banners
*/
public static final ItemLists BANNERS = newBuilder().endsWith("_BANNER").build();

/**
* List of wall coral fans, players can fall through these
*/
public static final ItemLists CORAL_FANS = newBuilder()
.endsWith("_WALL_FAN")
.build();

/**
* List of solid blocks
*/
public static final ItemLists NOT_SOLID_BLOCKS = newBuilder()
Intybyte marked this conversation as resolved.
Show resolved Hide resolved
.add("TRIPWIRE", "TRIPWIRE_HOOK")
.add("REDSTONE_WIRE","COMPARATOR","REPEATER","LEVER")
.includeList(CORAL_FANS)
.includeList(AIR_TYPES)
.includeList(SIGNS)
.includeList(BUTTONS)
.includeList(PRESSURE_PLATES)
.includeList(FENCE_GATES) //if open they are basically air
.includeList(TRAPDOORS) //if open they are basically air
.includeList(DOORS) ////if open they are basically air
.includeList(TORCHES)
.includeList(BANNERS)
.includeList(PLANTS).build();

public static final ItemLists FALLING_BLOCKS = newBuilder()
.add("SAND", "RED_SAND", "GRAVEL", "SUSPICIOUS_SAND", "SUSPICIOUS_GRAVEL")
.endsWith("_CONCRETE_POWDER")
.build();

Intybyte marked this conversation as resolved.
Show resolved Hide resolved
/**
* List of blocks which, when exploded, will not have their drops set to false, despite our asking.
*/
Expand Down
Loading