Support 0>y>256 for safe/random teleports (#4641)

Co-authored-by: pop4959 <pop4959@gmail.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
This commit is contained in:
MD 2021-11-24 04:44:08 +00:00
parent c0082ee734
commit 1509cf8978
13 changed files with 125 additions and 39 deletions

View file

@ -189,7 +189,7 @@ public class AsyncTeleport implements IAsyncTeleport {
}
PaperLib.getChunkAtAsync(targetLoc.getWorld(), targetLoc.getBlockX() >> 4, targetLoc.getBlockZ() >> 4, true, true).thenAccept(chunk -> {
Location loc = targetLoc;
if (LocationUtil.isBlockUnsafeForUser(teleportee, chunk.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (LocationUtil.isBlockUnsafeForUser(ess, teleportee, chunk.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (ess.getSettings().isTeleportSafetyEnabled()) {
if (ess.getSettings().isForceDisableTeleportSafety()) {
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.

View file

@ -69,16 +69,19 @@ import net.ess3.provider.SpawnEggProvider;
import net.ess3.provider.SpawnerBlockProvider;
import net.ess3.provider.SpawnerItemProvider;
import net.ess3.provider.SyncCommandsProvider;
import net.ess3.provider.WorldInfoProvider;
import net.ess3.provider.providers.BasePotionDataProvider;
import net.ess3.provider.providers.BlockMetaSpawnerItemProvider;
import net.ess3.provider.providers.BukkitMaterialTagProvider;
import net.ess3.provider.providers.BukkitSpawnerBlockProvider;
import net.ess3.provider.providers.FixedHeightWorldInfoProvider;
import net.ess3.provider.providers.FlatSpawnEggProvider;
import net.ess3.provider.providers.LegacyItemUnbreakableProvider;
import net.ess3.provider.providers.LegacyPotionMetaProvider;
import net.ess3.provider.providers.LegacySpawnEggProvider;
import net.ess3.provider.providers.ModernItemUnbreakableProvider;
import net.ess3.provider.providers.ModernPersistentDataProvider;
import net.ess3.provider.providers.ModernWorldInfoProvider;
import net.ess3.provider.providers.PaperContainerProvider;
import net.ess3.provider.providers.PaperKnownCommandsProvider;
import net.ess3.provider.providers.PaperMaterialTagProvider;
@ -170,6 +173,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
private transient PersistentDataProvider persistentDataProvider;
private transient ReflOnlineModeProvider onlineModeProvider;
private transient ItemUnbreakableProvider unbreakableProvider;
private transient WorldInfoProvider worldInfoProvider;
private transient Kits kits;
private transient RandomTeleport randomTeleport;
private transient UpdateChecker updateChecker;
@ -419,6 +423,12 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
unbreakableProvider = new LegacyItemUnbreakableProvider();
}
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_17_1_R01)) {
worldInfoProvider = new ModernWorldInfoProvider();
} else {
worldInfoProvider = new FixedHeightWorldInfoProvider();
}
execTimer.mark("Init(Providers)");
reload();
@ -1284,6 +1294,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
return unbreakableProvider;
}
@Override
public WorldInfoProvider getWorldInfoProvider() {
return worldInfoProvider;
}
@Override
public PluginCommand getPluginCommand(final String cmd) {
return this.getCommand(cmd);

View file

@ -217,7 +217,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
to.setY(from.getY());
to.setZ(from.getZ());
try {
event.setTo(LocationUtil.getSafeDestination(to));
event.setTo(LocationUtil.getSafeDestination(ess, to));
} catch (final Exception ex) {
event.setTo(to);
}
@ -412,7 +412,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
if (user.isAuthorized("essentials.fly.safelogin")) {
user.getBase().setFallDistance(0);
if (LocationUtil.shouldFly(user.getLocation())) {
if (LocationUtil.shouldFly(ess, user.getLocation())) {
user.getBase().setAllowFlight(true);
user.getBase().setFlying(true);
if (ess.getSettings().isSendFlyEnableOnJoin()) {

View file

@ -19,6 +19,7 @@ import net.ess3.provider.SerializationProvider;
import net.ess3.provider.SpawnerBlockProvider;
import net.ess3.provider.SpawnerItemProvider;
import net.ess3.provider.SyncCommandsProvider;
import net.ess3.provider.WorldInfoProvider;
import net.essentialsx.api.v2.services.BalanceTop;
import net.essentialsx.api.v2.services.mail.MailService;
import org.bukkit.Server;
@ -162,5 +163,7 @@ public interface IEssentials extends Plugin {
ItemUnbreakableProvider getItemUnbreakableProvider();
WorldInfoProvider getWorldInfoProvider();
PluginCommand getPluginCommand(String cmd);
}

View file

@ -22,12 +22,12 @@ import java.util.concurrent.ConcurrentLinkedQueue;
public class RandomTeleport implements IConf {
private static final Random RANDOM = new Random();
private static final int HIGHEST_BLOCK_Y_OFFSET = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_15_R01) ? 1 : 0;
private final IEssentials essentials;
private final IEssentials ess;
private final EssentialsConfiguration config;
private final ConcurrentLinkedQueue<Location> cachedLocations = new ConcurrentLinkedQueue<>();
public RandomTeleport(final IEssentials essentials) {
this.essentials = essentials;
this.ess = essentials;
config = new EssentialsConfiguration(new File(essentials.getDataFolder(), "tpr.yml"), "/tpr.yml",
"Configuration for the random teleport command.\nSome settings may be defaulted, and can be changed via the /settpr command in-game.");
reloadConfig();
@ -47,8 +47,8 @@ public class RandomTeleport implements IConf {
}
} catch (final InvalidWorldException ignored) {
}
final Location center = essentials.getServer().getWorlds().get(0).getWorldBorder().getCenter();
center.setY(center.getWorld().getHighestBlockYAt(center) + 1);
final Location center = ess.getServer().getWorlds().get(0).getWorldBorder().getCenter();
center.setY(center.getWorld().getHighestBlockYAt(center) + HIGHEST_BLOCK_Y_OFFSET);
setCenter(center);
return center;
}
@ -124,7 +124,7 @@ public class RandomTeleport implements IConf {
// Prompts caching random valid locations, up to a maximum number of attempts
public void cacheRandomLocations(final Location center, final double minRange, final double maxRange) {
essentials.getServer().getScheduler().scheduleSyncDelayedTask(essentials, () -> {
ess.getServer().getScheduler().scheduleSyncDelayedTask(ess, () -> {
for (int i = 0; i < this.getFindAttempts(); ++i) {
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
if (isValidRandomLocation(location)) {
@ -177,7 +177,7 @@ public class RandomTeleport implements IConf {
final Location location = new Location(
center.getWorld(),
center.getX() + offsetX,
center.getWorld().getMaxHeight(),
ess.getWorldInfoProvider().getMaxSafeHeight(center.getWorld()),
center.getZ() + offsetZ,
360 * RANDOM.nextFloat() - 180,
0
@ -195,8 +195,8 @@ public class RandomTeleport implements IConf {
// Returns an appropriate elevation for a given location in the nether, or -1 if none is found
private double getNetherYAt(final Location location) {
for (int y = 32; y < location.getWorld().getMaxHeight() / 2; ++y) {
if (!LocationUtil.isBlockUnsafe(location.getWorld(), location.getBlockX(), y, location.getBlockZ())) {
for (int y = 32; y < ess.getWorldInfoProvider().getMaxSafeHeight(location.getWorld()); ++y) {
if (!LocationUtil.isBlockUnsafe(ess, location.getWorld(), location.getBlockX(), y, location.getBlockZ())) {
return y;
}
}
@ -204,6 +204,6 @@ public class RandomTeleport implements IConf {
}
private boolean isValidRandomLocation(final Location location) {
return location.getBlockY() > 0 && !this.getExcludedBiomes().contains(location.getBlock().getBiome());
return location.getBlockY() > ess.getWorldInfoProvider().getMinSafeHeight(location.getWorld()) && !this.getExcludedBiomes().contains(location.getBlock().getBiome());
}
}

View file

@ -103,7 +103,7 @@ public final class SpawnMob {
// This method spawns a mob at loc, owned by target
public static void spawnmob(final IEssentials ess, final Server server, final CommandSource sender, final User target, final Location loc, final List<String> parts, final List<String> data, int mobCount) throws Exception {
final Location sloc = LocationUtil.getSafeDestination(loc);
final Location sloc = LocationUtil.getSafeDestination(ess, loc);
for (final String part : parts) {
final Mob mob = Mob.fromName(part);

View file

@ -145,7 +145,7 @@ public class Teleport implements ITeleport {
teleportee.getBase().eject();
}
if (LocationUtil.isBlockUnsafeForUser(teleportee, loc.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (LocationUtil.isBlockUnsafeForUser(ess, teleportee, loc.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (ess.getSettings().isTeleportSafetyEnabled()) {
if (ess.getSettings().isForceDisableTeleportSafety()) {
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);

View file

@ -51,7 +51,7 @@ public class Commandsethome extends EssentialsCommand {
}
final Location location = user.getLocation();
if ((!ess.getSettings().isTeleportSafetyEnabled() || !ess.getSettings().isForceDisableTeleportSafety()) && LocationUtil.isBlockUnsafeForUser(usersHome, location.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ())) {
if ((!ess.getSettings().isTeleportSafetyEnabled() || !ess.getSettings().isForceDisableTeleportSafety()) && LocationUtil.isBlockUnsafeForUser(ess, usersHome, location.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ())) {
throw new Exception(tl("unsafeTeleportDestination", location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()));
}

View file

@ -22,13 +22,14 @@ public class Commandtop extends EssentialsCommand {
final int topZ = user.getLocation().getBlockZ();
final float pitch = user.getLocation().getPitch();
final float yaw = user.getLocation().getYaw();
final Location loc = LocationUtil.getSafeDestination(new Location(user.getWorld(), topX, user.getWorld().getMaxHeight(), topZ, yaw, pitch));
final Location unsafe = new Location(user.getWorld(), topX, ess.getWorldInfoProvider().getMaxSafeHeight(user.getWorld()), topZ, yaw, pitch);
final Location safe = LocationUtil.getSafeDestination(ess, unsafe);
final CompletableFuture<Boolean> future = new CompletableFuture<>();
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleportTop", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
user.sendMessage(tl("teleportTop", safe.getWorld().getName(), safe.getBlockX(), safe.getBlockY(), safe.getBlockZ()));
}
});
user.getAsyncTeleport().teleport(loc, new Trade(this.getName(), ess), TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
user.getAsyncTeleport().teleport(safe, new Trade(this.getName(), ess), TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
}

View file

@ -1,6 +1,7 @@
package com.earth2me.essentials.utils;
import com.earth2me.essentials.IEssentials;
import com.google.common.primitives.Ints;
import net.ess3.api.IUser;
import org.bukkit.GameMode;
import org.bukkit.Location;
@ -95,8 +96,8 @@ public final class LocationUtil {
return block.getLocation();
}
public static boolean isBlockAboveAir(final World world, final int x, final int y, final int z) {
return y > world.getMaxHeight() || HOLLOW_MATERIALS.contains(world.getBlockAt(x, y - 1, z).getType());
public static boolean isBlockAboveAir(IEssentials ess, final World world, final int x, final int y, final int z) {
return y > ess.getWorldInfoProvider().getMaxSafeHeight(world) || HOLLOW_MATERIALS.contains(world.getBlockAt(x, y - 1, z).getType());
}
public static boolean isBlockOutsideWorldBorder(final World world, final int x, final int z) {
@ -131,7 +132,7 @@ public final class LocationUtil {
return z;
}
public static boolean isBlockUnsafeForUser(final IUser user, final World world, final int x, final int y, final int z) {
public static boolean isBlockUnsafeForUser(IEssentials ess, final IUser user, final World world, final int x, final int y, final int z) {
if (user.getBase().isOnline() && world.equals(user.getBase().getWorld()) && (user.getBase().getGameMode() == GameMode.CREATIVE || user.getBase().getGameMode() == GameMode.SPECTATOR || user.isGodModeEnabled()) && user.getBase().getAllowFlight()) {
return false;
}
@ -139,14 +140,14 @@ public final class LocationUtil {
if (isBlockDamaging(world, x, y, z)) {
return true;
}
if (isBlockAboveAir(world, x, y, z)) {
if (isBlockAboveAir(ess, world, x, y, z)) {
return true;
}
return isBlockOutsideWorldBorder(world, x, z);
}
public static boolean isBlockUnsafe(final World world, final int x, final int y, final int z) {
return isBlockDamaging(world, x, y, z) || isBlockAboveAir(world, x, y, z);
public static boolean isBlockUnsafe(IEssentials ess, final World world, final int x, final int y, final int z) {
return isBlockDamaging(world, x, y, z) || isBlockAboveAir(ess, world, x, y, z);
}
public static boolean isBlockDamaging(final World world, final int x, final int y, final int z) {
@ -184,7 +185,7 @@ public final class LocationUtil {
public static Location getSafeDestination(final IEssentials ess, final IUser user, final Location loc) throws Exception {
if (user.getBase().isOnline() && (ess == null || !ess.getSettings().isAlwaysTeleportSafety()) && (user.getBase().getGameMode() == GameMode.CREATIVE || user.getBase().getGameMode() == GameMode.SPECTATOR || user.isGodModeEnabled())) {
if (shouldFly(loc) && user.getBase().getAllowFlight()) {
if (shouldFly(ess, loc) && user.getBase().getAllowFlight()) {
user.getBase().setFlying(true);
}
// ess can be null if old deprecated method is calling it.
@ -194,14 +195,16 @@ public final class LocationUtil {
return loc;
}
}
return getSafeDestination(loc);
return getSafeDestination(ess, loc);
}
public static Location getSafeDestination(final Location loc) throws Exception {
public static Location getSafeDestination(IEssentials ess, final Location loc) throws Exception {
if (loc == null || loc.getWorld() == null) {
throw new Exception(tl("destinationNotSet"));
}
final World world = loc.getWorld();
final int worldMinY = ess.getWorldInfoProvider().getMinSafeHeight(world);
final int worldMaxY = ess.getWorldInfoProvider().getMaxSafeHeight(world);
int x = loc.getBlockX();
int y = (int) Math.round(loc.getY());
int z = loc.getBlockZ();
@ -212,42 +215,43 @@ public final class LocationUtil {
final int origX = x;
final int origY = y;
final int origZ = z;
while (isBlockAboveAir(world, x, y, z)) {
while (isBlockAboveAir(ess, world, x, y, z)) {
y -= 1;
if (y < 0) {
y = origY;
break;
}
}
if (isBlockUnsafe(world, x, y, z)) {
if (isBlockUnsafe(ess, world, x, y, z)) {
x = Math.round(loc.getX()) == origX ? x - 1 : x + 1;
z = Math.round(loc.getZ()) == origZ ? z - 1 : z + 1;
}
int i = 0;
while (isBlockUnsafe(world, x, y, z)) {
while (isBlockUnsafe(ess, world, x, y, z)) {
i++;
if (i >= VOLUME.length) {
x = origX;
y = origY + RADIUS;
y = Ints.constrainToRange(origY + RADIUS, worldMinY, worldMaxY);
z = origZ;
break;
}
x = origX + VOLUME[i].x;
y = origY + VOLUME[i].y;
y = Ints.constrainToRange(origY + VOLUME[i].y, worldMinY, worldMaxY);
z = origZ + VOLUME[i].z;
}
while (isBlockUnsafe(world, x, y, z)) {
while (isBlockUnsafe(ess, world, x, y, z)) {
y += 1;
if (y >= world.getMaxHeight()) {
if (y >= worldMaxY) {
x += 1;
break;
}
}
while (isBlockUnsafe(world, x, y, z)) {
while (isBlockUnsafe(ess, world, x, y, z)) {
y -= 1;
if (y <= 1) {
if (y <= worldMinY + 1) {
x += 1;
y = world.getHighestBlockYAt(x, z);
// Allow spawning at the top of the world, but not above the nether roof
y = Math.min(world.getHighestBlockYAt(x, z) + 1, worldMaxY);
if (x - 48 > loc.getBlockX()) {
throw new Exception(tl("holeInFloor"));
}
@ -256,13 +260,13 @@ public final class LocationUtil {
return new Location(world, x + 0.5, y, z + 0.5, loc.getYaw(), loc.getPitch());
}
public static boolean shouldFly(final Location loc) {
public static boolean shouldFly(IEssentials ess, final Location loc) {
final World world = loc.getWorld();
final int x = loc.getBlockX();
int y = (int) Math.round(loc.getY());
final int z = loc.getBlockZ();
int count = 0;
while (LocationUtil.isBlockUnsafe(world, x, y, z) && y > -1) {
while (LocationUtil.isBlockUnsafe(ess, world, x, y, z) && y > -1) {
y--;
count++;
if (count > 2) {

View file

@ -0,0 +1,21 @@
package net.ess3.provider;
import org.bukkit.World;
public interface WorldInfoProvider extends Provider {
/**
* Gets the maximum safe height for teleportation.
*
* @param world The world of which to check the maximum safe height.
* @return The maximum safe height for teleportation
*/
int getMaxSafeHeight(World world);
/**
* Gets the minimum safe height for teleportation.
*
* @param world The world of which to check the base height.
* @return The minimum safe height for teleportation
*/
int getMinSafeHeight(World world);
}

View file

@ -0,0 +1,21 @@
package net.ess3.provider.providers;
import net.ess3.provider.WorldInfoProvider;
import org.bukkit.World;
public class FixedHeightWorldInfoProvider implements WorldInfoProvider {
@Override
public String getDescription() {
return "World info provider for fixed world heights";
}
@Override
public int getMaxSafeHeight(World world) {
return world.getEnvironment() == World.Environment.NETHER ? 128 : 256;
}
@Override
public int getMinSafeHeight(World world) {
return 0;
}
}

View file

@ -0,0 +1,21 @@
package net.ess3.provider.providers;
import net.ess3.provider.WorldInfoProvider;
import org.bukkit.World;
public class ModernWorldInfoProvider implements WorldInfoProvider {
@Override
public String getDescription() {
return "World info provider for data-driven world generation";
}
@Override
public int getMaxSafeHeight(World world) {
return world.getLogicalHeight();
}
@Override
public int getMinSafeHeight(World world) {
return world.getMinHeight();
}
}