mirror of
https://github.com/TotalFreedomMC/TF-EssentialsX.git
synced 2024-06-28 08:50:57 +00:00
Implement random teleport command (#3418)
Adds `/tpr` and `/settpr` commands, which respectively allow you to teleport randomly or set teleportation parameters. Server owners are expected to set the center with `/settpr` before players can use `/tpr`. They can also set the minimum and maximum range to be teleported from the center (default 0-1000). Also includes an event where plugins can adjust or cancel the teleport. Closes #3154.
This commit is contained in:
parent
9681933ec2
commit
76e511a774
|
@ -113,6 +113,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||
private transient ServerStateProvider serverStateProvider;
|
||||
private transient ProviderListener recipeBookEventProvider;
|
||||
private transient Kits kits;
|
||||
private transient RandomTeleport randomTeleport;
|
||||
|
||||
public Essentials() {
|
||||
|
||||
|
@ -222,6 +223,13 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||
confList.add(itemDb);
|
||||
execTimer.mark("Init(ItemDB)");
|
||||
|
||||
randomTeleport = new RandomTeleport(this);
|
||||
if (randomTeleport.getPreCache()) {
|
||||
randomTeleport.cacheRandomLocations(randomTeleport.getCenter(), randomTeleport.getMinRange(), randomTeleport.getMaxRange());
|
||||
}
|
||||
confList.add(randomTeleport);
|
||||
execTimer.mark("Init(RandomTeleport)");
|
||||
|
||||
customItemResolver = new CustomItemResolver(this);
|
||||
try {
|
||||
itemDb.registerResolver(this, "custom_items", customItemResolver);
|
||||
|
@ -682,6 +690,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||
return kits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomTeleport getRandomTeleport() {
|
||||
return randomTeleport;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public User getUser(final Object base) {
|
||||
|
|
|
@ -66,6 +66,8 @@ public interface IEssentials extends Plugin {
|
|||
|
||||
Kits getKits();
|
||||
|
||||
RandomTeleport getRandomTeleport();
|
||||
|
||||
Methods getPaymentMethod();
|
||||
|
||||
BukkitTask runTaskAsynchronously(Runnable run);
|
||||
|
|
175
Essentials/src/com/earth2me/essentials/RandomTeleport.java
Normal file
175
Essentials/src/com/earth2me/essentials/RandomTeleport.java
Normal file
|
@ -0,0 +1,175 @@
|
|||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.ess3.api.InvalidWorldException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class RandomTeleport implements IConf {
|
||||
private final IEssentials essentials;
|
||||
private final EssentialsConf config;
|
||||
private final ConcurrentLinkedQueue<Location> cachedLocations = new ConcurrentLinkedQueue<>();
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int HIGHEST_BLOCK_Y_OFFSET = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_15_R01) ? 1 : 0;
|
||||
|
||||
public RandomTeleport(final IEssentials essentials) {
|
||||
this.essentials = essentials;
|
||||
File file = new File(essentials.getDataFolder(), "tpr.yml");
|
||||
config = new EssentialsConf(file);
|
||||
config.setTemplateName("/tpr.yml");
|
||||
config.options().copyHeader(true);
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
config.load();
|
||||
cachedLocations.clear();
|
||||
}
|
||||
|
||||
public Location getCenter() {
|
||||
try {
|
||||
Location center = config.getLocation("center", essentials.getServer());
|
||||
if (center != null) {
|
||||
return center;
|
||||
}
|
||||
} catch (InvalidWorldException ignored) {
|
||||
}
|
||||
Location center = essentials.getServer().getWorlds().get(0).getWorldBorder().getCenter();
|
||||
center.setY(center.getWorld().getHighestBlockYAt(center) + 1);
|
||||
setCenter(center);
|
||||
return center;
|
||||
}
|
||||
|
||||
public void setCenter(Location center) {
|
||||
config.setProperty("center", center);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public double getMinRange() {
|
||||
return config.getDouble("min-range", 0d);
|
||||
}
|
||||
|
||||
public void setMinRange(double minRange) {
|
||||
config.setProperty("min-range", minRange);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public double getMaxRange() {
|
||||
return config.getDouble("max-range", getCenter().getWorld().getWorldBorder().getSize() / 2);
|
||||
}
|
||||
|
||||
public void setMaxRange(double maxRange) {
|
||||
config.setProperty("max-range", maxRange);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public Set<Biome> getExcludedBiomes() {
|
||||
List<String> biomeNames = config.getStringList("excluded-biomes");
|
||||
Set<Biome> excludedBiomes = new HashSet<>();
|
||||
for (String biomeName : biomeNames) {
|
||||
try {
|
||||
excludedBiomes.add(Biome.valueOf(biomeName.toUpperCase()));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
return excludedBiomes;
|
||||
}
|
||||
|
||||
public int getFindAttempts() {
|
||||
return config.getInt("find-attempts", 10);
|
||||
}
|
||||
|
||||
public int getCacheThreshold() {
|
||||
return config.getInt("cache-threshold", 10);
|
||||
}
|
||||
|
||||
public boolean getPreCache() {
|
||||
return config.getBoolean("pre-cache", true);
|
||||
}
|
||||
|
||||
public Queue<Location> getCachedLocations() {
|
||||
return cachedLocations;
|
||||
}
|
||||
|
||||
// Get a random location; cached if possible. Otherwise on demand.
|
||||
public CompletableFuture<Location> getRandomLocation(Location center, double minRange, double maxRange) {
|
||||
int findAttempts = this.getFindAttempts();
|
||||
Queue<Location> cachedLocations = this.getCachedLocations();
|
||||
// Try to build up the cache if it is below the threshold
|
||||
if (cachedLocations.size() < this.getCacheThreshold()) {
|
||||
cacheRandomLocations(center, minRange, maxRange);
|
||||
}
|
||||
CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
// Return a random location immediately if one is available, otherwise try to find one now
|
||||
if (cachedLocations.isEmpty()) {
|
||||
attemptRandomLocation(findAttempts, center, minRange, maxRange).thenAccept(future::complete);
|
||||
} else {
|
||||
future.complete(cachedLocations.poll());
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
// Prompts caching random valid locations, up to a maximum number of attempts
|
||||
public void cacheRandomLocations(Location center, double minRange, double maxRange) {
|
||||
essentials.getServer().getScheduler().scheduleSyncDelayedTask(essentials, () -> {
|
||||
for (int i = 0; i < this.getFindAttempts(); ++i) {
|
||||
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
|
||||
if (isValidRandomLocation(location)) {
|
||||
this.getCachedLocations().add(location);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Recursively attempt to find a random location. After a maximum number of attempts, the center is returned.
|
||||
private CompletableFuture<Location> attemptRandomLocation(int attempts, Location center, double minRange, double maxRange) {
|
||||
CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
if (attempts > 0) {
|
||||
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
|
||||
if (isValidRandomLocation(location)) {
|
||||
future.complete(location);
|
||||
} else {
|
||||
attemptRandomLocation(attempts - 1, center, minRange, maxRange).thenAccept(future::complete);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
future.complete(center);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
// Calculates a random location asynchronously.
|
||||
private CompletableFuture<Location> calculateRandomLocation(Location center, double minRange, double maxRange) {
|
||||
CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
final int dx = RANDOM.nextBoolean() ? 1 : -1, dz = RANDOM.nextBoolean() ? 1 : -1;
|
||||
Location location = new Location(
|
||||
center.getWorld(),
|
||||
center.getX() + dx * (minRange + RANDOM.nextDouble() * (maxRange - minRange)),
|
||||
center.getWorld().getMaxHeight(),
|
||||
center.getZ() + dz * (minRange + RANDOM.nextDouble() * (maxRange - minRange)),
|
||||
360 * RANDOM.nextFloat() - 180,
|
||||
0
|
||||
);
|
||||
PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
|
||||
location.setY(center.getWorld().getHighestBlockYAt(location) + HIGHEST_BLOCK_Y_OFFSET);
|
||||
future.complete(location);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private boolean isValidRandomLocation(Location location) {
|
||||
return !this.getExcludedBiomes().contains(location.getBlock().getBiome());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.RandomTeleport;
|
||||
import com.earth2me.essentials.User;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
|
||||
public class Commandsettpr extends EssentialsCommand {
|
||||
public Commandsettpr() {
|
||||
super("settpr");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
|
||||
RandomTeleport randomTeleport = ess.getRandomTeleport();
|
||||
randomTeleport.getCachedLocations().clear();
|
||||
if (args.length == 0 || "center".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setCenter(user.getLocation());
|
||||
user.sendMessage(tl("settpr"));
|
||||
} else if (args.length > 1) {
|
||||
if ("minrange".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setMinRange(Double.parseDouble(args[1]));
|
||||
} else if ("maxrange".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setMaxRange(Double.parseDouble(args[1]));
|
||||
}
|
||||
user.sendMessage(tl("settprValue", args[0].toLowerCase(), args[1].toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("center", "minrange", "maxrange");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.RandomTeleport;
|
||||
import com.earth2me.essentials.Trade;
|
||||
import com.earth2me.essentials.User;
|
||||
import net.ess3.api.events.UserRandomTeleportEvent;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
|
||||
public class Commandtpr extends EssentialsCommand {
|
||||
|
||||
public Commandtpr() {
|
||||
super("tpr");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
|
||||
final Trade charge = new Trade(this.getName(), ess);
|
||||
charge.isAffordableFor(user);
|
||||
RandomTeleport randomTeleport = ess.getRandomTeleport();
|
||||
UserRandomTeleportEvent event = new UserRandomTeleportEvent(user, randomTeleport.getCenter(), randomTeleport.getMinRange(), randomTeleport.getMaxRange());
|
||||
server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
randomTeleport.getRandomLocation(event.getCenter(), event.getMinRange(), event.getMaxRange()).thenAccept(location -> {
|
||||
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
|
||||
user.getAsyncTeleport().teleport(location, charge, PlayerTeleportEvent.TeleportCause.COMMAND, future);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
user.sendMessage(tl("tprSuccess"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -694,6 +694,10 @@ sethomeCommandDescription=Set your home to your current location.
|
|||
sethomeCommandUsage=/<command> [[player:]name]
|
||||
setjailCommandDescription=Creates a jail where you specified named [jailname].
|
||||
setjailCommandUsage=/<command> <jailname>
|
||||
settprCommandDescription=Set the random teleport location and parameters.
|
||||
settprCommandUsage=/<command> [center|minrange|maxrange] [value]
|
||||
settpr=\u00a76Set random teleport center.
|
||||
settprValue=\u00a76Set random teleport \u00a7c{0}\u00a76 to \u00a7c{1}\u00a76.
|
||||
setwarpCommandDescription=Creates a new warp.
|
||||
setwarpCommandUsage=/<command> <warp>
|
||||
setworthCommandDescription=Set the sell value of an item.
|
||||
|
@ -830,6 +834,9 @@ tpohereCommandDescription=Teleport here override for tptoggle.
|
|||
tpohereCommandUsage=/<command> <player>
|
||||
tpposCommandDescription=Teleport to coordinates.
|
||||
tpposCommandUsage=/<command> <x> <y> <z> [yaw] [pitch] [world]
|
||||
tprCommandDescription=Teleport randomly.
|
||||
tprCommandUsage=/<command>
|
||||
tprSuccess=\u00a76Teleporting to a random location...
|
||||
tps=\u00a76Current TPS \= {0}
|
||||
tptoggleCommandDescription=Blocks all forms of teleportation.
|
||||
tptoggleCommandUsage=/<command> [player] [on|off]
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package net.ess3.api.events;
|
||||
|
||||
import net.ess3.api.IUser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Called when the player uses the command /tpr
|
||||
*/
|
||||
public class UserRandomTeleportEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private IUser user;
|
||||
private Location center;
|
||||
private double minRange, maxRange;
|
||||
private boolean cancelled = false;
|
||||
|
||||
public UserRandomTeleportEvent(IUser user, Location center, double minRange, double maxRange) {
|
||||
super(!Bukkit.isPrimaryThread());
|
||||
this.user = user;
|
||||
this.center = center;
|
||||
this.minRange = minRange;
|
||||
this.maxRange = maxRange;
|
||||
}
|
||||
|
||||
public IUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public Location getCenter() {
|
||||
return center;
|
||||
}
|
||||
|
||||
public void setCenter(Location center) {
|
||||
this.center = center;
|
||||
}
|
||||
|
||||
public double getMinRange() {
|
||||
return minRange;
|
||||
}
|
||||
|
||||
public void setMinRange(double minRange) {
|
||||
this.minRange = minRange;
|
||||
}
|
||||
|
||||
public double getMaxRange() {
|
||||
return maxRange;
|
||||
}
|
||||
|
||||
public void setMaxRange(double maxRange) {
|
||||
this.maxRange = maxRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean b) {
|
||||
cancelled = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
|
@ -388,6 +388,10 @@ commands:
|
|||
description: Creates a jail where you specified named [jailname].
|
||||
usage: /<command> <jailname>
|
||||
aliases: [esetjail,createjail,ecreatejail]
|
||||
settpr:
|
||||
description: Set the random teleport location and parameters.
|
||||
usage: /<command> [center|minrange|maxrange] [value]
|
||||
aliases: [esettpr, settprandom, esettprandom]
|
||||
setwarp:
|
||||
description: Creates a new warp.
|
||||
usage: /<command> <warp>
|
||||
|
@ -508,6 +512,10 @@ commands:
|
|||
description: Teleport to coordinates.
|
||||
usage: /<command> <x> <y> <z> [yaw] [pitch] [world]
|
||||
aliases: [etppos]
|
||||
tpr:
|
||||
description: Teleport randomly.
|
||||
usage: /<command>
|
||||
aliases: [etpr, tprandom, etprandom]
|
||||
tptoggle:
|
||||
description: Blocks all forms of teleportation.
|
||||
usage: /<command> [player] [on|off]
|
||||
|
|
16
Essentials/src/tpr.yml
Normal file
16
Essentials/src/tpr.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Configuration for the random teleport command.
|
||||
# Some settings may be defaulted, and can be changed via the /settpr command in-game.
|
||||
min-range: 0.0
|
||||
excluded-biomes:
|
||||
- cold_ocean
|
||||
- deep_cold_ocean
|
||||
- deep_frozen_ocean
|
||||
- deep_lukewarm_ocean
|
||||
- deep_ocean
|
||||
- deep_warm_ocean
|
||||
- frozen_ocean
|
||||
- frozen_river
|
||||
- lukewarm_ocean
|
||||
- ocean
|
||||
- river
|
||||
- warm_ocean
|
Loading…
Reference in a new issue