Reduce sync loads for teleporting (#3102)

This PR reduces the number of sync loads occurring on any teleport caused by essentials.

Fixes #2861
Fixes #2287
Fixes #3274
Fixes #3201
Fixes #2120

Before this PR, essentials would get a block multiple times causing sync loads to check if it was safe to teleport to. Now, the target block's chunk if fetched async with PaperLib and passed along to `LocationUtil#isBlockUnsafeForUser` (which internally calls other LocationUtil methods what that chunk object) resulting in the chunk only loading once, off the main thread. The only operations remaining on the main thread is `LocationUtil#getSafeDestination`. This is due to the method's recursion which would be a pain to move async. **However:** since the chunk was already loaded async, `LocationUtil#getSafeDestination` most of the time won't cause sync chunk loads. The only time it would cause sync chunk loads is with an unsafe location near a chunk border.

-----------------------------------------

* Reduce sync teleporting loads

* Avoid argument re-assigning

* Remove async teleports when unnecessary

* Make exceptions cleaner

* Async all the things

Made an async version of every method with fallbacks for deprecated methods.

* Remove old now fallback method

* Migrate everything to the new async teleport API

* Update ITeleport javadocs

* Fix invoking via async context

* Fix /jail using deprecated method

* Fix jail join handler using deprecated method

* Rename all teleport classes to indicate async

* Remove deprecated methods

* Add (and deprecate) old teleport api

* Revert TimedTeleport.java

* Reduce Diff

* Add legacy sendToJail method

* Reduce Diff Further

* Use getNewExceptionFuture in Commandtpo

* Use getNewExceptionFuture everywhere

* Fix even more usages

* Revert LocationUtil.java

* Fix issue causing unsafe locations to not work properly

* Add deprecated notice in IUser implementation

* Use CompletableFuture#completeExceptionally for exceptions

* Use Essentials' logger in EssentialsCommand#showError

* Return implementation rather than interface

* Avoid possible deadlocks with entity ejections

* Nuke some sync loads with homes

Took 7 hours and 2 PRs to paper but it's here!

* Fix ABI and make the codestyle worse

* Make the codestyle worse because muh diff

* Further ruin the codestyle

* Fix error messages not showing in TimedTeleports

* Improve messages around beds for /home

* Fix #3274

Allow unsafe locations for different worlds + spectator mode

* Fix fly safety operators
This commit is contained in:
Josh Roy 2020-06-24 04:52:25 -04:00 committed by GitHub
parent d2f2140be9
commit d9bf099c3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1149 additions and 191 deletions

View File

@ -63,7 +63,7 @@
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -0,0 +1,428 @@
package com.earth2me.essentials;
import com.earth2me.essentials.api.IAsyncTeleport;
import com.earth2me.essentials.commands.WarpNotFoundException;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.LocationUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import net.ess3.api.InvalidWorldException;
import net.ess3.api.events.UserTeleportEvent;
import net.ess3.api.events.UserWarpEvent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.earth2me.essentials.I18n.tl;
public class AsyncTeleport implements IAsyncTeleport {
private final IUser teleportOwner;
private final IEssentials ess;
private AsyncTimedTeleport timedTeleport;
private TeleportType tpType;
public AsyncTeleport(IUser user, IEssentials ess) {
this.teleportOwner = user;
this.ess = ess;
tpType = TeleportType.NORMAL;
}
public enum TeleportType {
TPA,
BACK,
NORMAL
}
public void cooldown(boolean check) throws Throwable {
CompletableFuture<Boolean> exceptionFuture = new CompletableFuture<>();
if (cooldown(check, exceptionFuture)) {
try {
exceptionFuture.get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
}
public boolean cooldown(boolean check, CompletableFuture<Boolean> future) {
final Calendar time = new GregorianCalendar();
if (teleportOwner.getLastTeleportTimestamp() > 0) {
// Take the current time, and remove the delay from it.
final double cooldown = ess.getSettings().getTeleportCooldown();
final Calendar earliestTime = new GregorianCalendar();
earliestTime.add(Calendar.SECOND, -(int) cooldown);
earliestTime.add(Calendar.MILLISECOND, -(int) ((cooldown * 1000.0) % 1000.0));
// This value contains the most recent time a teleportPlayer could have been used that would allow another use.
final long earliestLong = earliestTime.getTimeInMillis();
// When was the last teleportPlayer used?
final long lastTime = teleportOwner.getLastTeleportTimestamp();
if (lastTime > time.getTimeInMillis()) {
// This is to make sure time didn't get messed up on last teleportPlayer use.
// If this happens, let's give the user the benifit of the doubt.
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
return false;
} else if (lastTime > earliestLong
&& cooldownApplies()) {
time.setTimeInMillis(lastTime);
time.add(Calendar.SECOND, (int) cooldown);
time.add(Calendar.MILLISECOND, (int) ((cooldown * 1000.0) % 1000.0));
future.completeExceptionally(new Exception(tl("timeBeforeTeleport", DateUtil.formatDateDiff(time.getTimeInMillis()))));
return true;
}
}
// if justCheck is set, don't update lastTeleport; we're just checking
if (!check) {
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
}
return false;
}
private boolean cooldownApplies() {
boolean applies = true;
String globalBypassPerm = "essentials.teleport.cooldown.bypass";
switch (tpType) {
case NORMAL:
applies = !teleportOwner.isAuthorized(globalBypassPerm);
break;
case BACK:
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.back"));
break;
case TPA:
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.tpa"));
break;
}
return applies;
}
private void warnUser(final IUser user, final double delay) {
Calendar c = new GregorianCalendar();
c.add(Calendar.SECOND, (int) delay);
c.add(Calendar.MILLISECOND, (int) ((delay * 1000.0) % 1000.0));
user.sendMessage(tl("dontMoveMessage", DateUtil.formatDateDiff(c.getTimeInMillis())));
}
@Override
public void now(Location loc, boolean cooldown, TeleportCause cause, CompletableFuture<Boolean> future) {
if (cooldown && cooldown(false, future)) {
return;
}
final ITarget target = new LocationTarget(loc);
nowAsync(teleportOwner, target, cause, future);
}
@Override
public void now(Player entity, boolean cooldown, TeleportCause cause, CompletableFuture<Boolean> future) {
if (cooldown && cooldown(false, future)) {
future.complete(false);
return;
}
final ITarget target = new PlayerTarget(entity);
nowAsync(teleportOwner, target, cause, future);
future.thenAccept(success -> {
if (success) {
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
});
}
private void runOnMain(Runnable runnable) throws ExecutionException, InterruptedException {
if (Bukkit.isPrimaryThread()) {
runnable.run();
return;
}
CompletableFuture<Object> taskLock = new CompletableFuture<>();
Bukkit.getScheduler().runTask(ess, () -> {
runnable.run();
taskLock.complete(new Object());
});
taskLock.get();
}
protected void nowAsync(IUser teleportee, ITarget target, TeleportCause cause, CompletableFuture<Boolean> future) {
cancel(false);
UserTeleportEvent event = new UserTeleportEvent(teleportee, cause, target.getLocation());
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
teleportee.setLastLocation();
if (!teleportee.getBase().isEmpty()) {
if (!ess.getSettings().isTeleportPassengerDismount()) {
future.completeExceptionally(new Exception(tl("passengerTeleportFail")));
return;
}
try {
runOnMain(() -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
} catch (ExecutionException | InterruptedException e) {
future.completeExceptionally(e);
return;
}
}
teleportee.setLastLocation();
PaperLib.getChunkAtAsync(target.getLocation()).thenAccept(chunk -> {
Location loc = target.getLocation();
if (LocationUtil.isBlockUnsafeForUser(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.
teleportee.getBase().teleport(loc, cause);
} else {
try {
//There's a chance the safer location is outside the loaded chunk so still teleport async here.
PaperLib.teleportAsync(teleportee.getBase(), LocationUtil.getSafeDestination(ess, teleportee, loc), cause);
} catch (Exception e) {
future.completeExceptionally(e);
return;
}
}
} else {
future.completeExceptionally(new Exception(tl("unsafeTeleportDestination", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())));
return;
}
} else {
if (ess.getSettings().isForceDisableTeleportSafety()) {
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.
teleportee.getBase().teleport(loc, cause);
} else {
if (ess.getSettings().isTeleportToCenterLocation()) {
loc = LocationUtil.getRoundedDestination(loc);
}
//There's a *small* chance the rounded destination produces a location outside the loaded chunk so still teleport async here.
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
}
}
future.complete(true);
});
}
@Override
public void teleport(Location loc, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
teleport(teleportOwner, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void teleport(Player entity, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
teleportOwner.sendMessage(tl("teleportToPlayer", entity.getDisplayName()));
teleport(teleportOwner, new PlayerTarget(entity), chargeFor, cause, future);
}
@Override
public void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
teleport(otherUser, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
ITarget target = new PlayerTarget(entity);
teleport(otherUser, target, chargeFor, cause, future);
future.thenAccept(success -> {
if (success) {
otherUser.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
});
}
private void teleport(IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
Trade cashCharge = chargeFor;
if (chargeFor != null) {
chargeFor.isAffordableFor(teleportOwner, future);
if (future.isCompletedExceptionally()) {
return;
}
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
if (!chargeFor.getCommandCost(teleportOwner).equals(BigDecimal.ZERO)) {
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
cashCharge = new Trade(chargeFor.getCommandCost(teleportOwner), ess);
}
}
if (cooldown(true, future)) {
return;
}
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass") || teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
if (cooldown(false, future)) {
return;
}
nowAsync(teleportee, target, cause, future);
if (cashCharge != null) {
cashCharge.charge(teleportOwner, future);
if (future.isCompletedExceptionally()) {
return;
}
}
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
}
private void teleportOther(IUser teleporter, IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
Trade cashCharge = chargeFor;
if (teleporter != null && chargeFor != null) {
chargeFor.isAffordableFor(teleporter, future);
if (future.isCompletedExceptionally()) {
return;
}
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
if (!chargeFor.getCommandCost(teleporter).equals(BigDecimal.ZERO)) {
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
cashCharge = new Trade(chargeFor.getCommandCost(teleporter), ess);
}
}
if (cooldown(true, future)) {
return;
}
if (delay <= 0 || teleporter == null
|| teleporter.isAuthorized("essentials.teleport.timer.bypass")
|| teleportOwner.isAuthorized("essentials.teleport.timer.bypass")
|| teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
if (cooldown(false, future)) {
return;
}
nowAsync(teleportee, target, cause, future);
if (teleporter != null && cashCharge != null) {
cashCharge.charge(teleporter, future);
if (future.isCompletedExceptionally()) {
return;
}
}
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
}
@Override
public void respawn(Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
if (chargeFor != null) {
chargeFor.isAffordableFor(teleportOwner, future);
if (future.isCompletedExceptionally()) {
return;
}
}
if (cooldown(true, future)) {
return;
}
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass")) {
if (cooldown(false, future)) {
return;
}
respawnNow(teleportOwner, cause, future);
if (chargeFor != null) {
chargeFor.charge(teleportOwner, future);
}
return;
}
cancel(false);
warnUser(teleportOwner, delay);
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true);
}
void respawnNow(IUser teleportee, TeleportCause cause, CompletableFuture<Boolean> future) {
final Player player = teleportee.getBase();
Location bed = player.getBedSpawnLocation();
if (bed != null) {
nowAsync(teleportee, new LocationTarget(bed), cause, future);
} else {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Could not find bed spawn, forcing respawn event.");
}
final PlayerRespawnEvent pre = new PlayerRespawnEvent(player, player.getWorld().getSpawnLocation(), false);
ess.getServer().getPluginManager().callEvent(pre);
nowAsync(teleportee, new LocationTarget(pre.getRespawnLocation()), cause, future);
}
}
@Override
public void warp(IUser otherUser, String warp, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
UserWarpEvent event = new UserWarpEvent(otherUser, warp, chargeFor);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
warp = event.getWarp();
Location loc;
try {
loc = ess.getWarps().getWarp(warp);
} catch (WarpNotFoundException | InvalidWorldException e) {
future.completeExceptionally(e);
return;
}
otherUser.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
if (!otherUser.equals(teleportOwner)) {
teleportOwner.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
teleport(otherUser, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void back(Trade chargeFor, CompletableFuture<Boolean> future) {
back(teleportOwner, chargeFor, future);
}
@Override
public void back(IUser teleporter, Trade chargeFor, CompletableFuture<Boolean> future) {
tpType = TeleportType.BACK;
final Location loc = teleportOwner.getLastLocation();
teleportOwner.sendMessage(tl("backUsageMsg", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
teleportOther(teleporter, teleportOwner, new LocationTarget(loc), chargeFor, TeleportCause.COMMAND, future);
}
@Override
public void back(CompletableFuture<Boolean> future) {
nowAsync(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND, future);
}
public void setTpType(TeleportType tpType) {
this.tpType = tpType;
}
//If we need to cancelTimer a pending teleportPlayer call this method
private void cancel(boolean notifyUser) {
if (timedTeleport != null) {
timedTeleport.cancelTimer(notifyUser);
timedTeleport = null;
}
}
private void initTimer(long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
timedTeleport = new AsyncTimedTeleport(teleportOwner, ess, this, delay, teleportUser, target, chargeFor, cause, respawn);
}
}

View File

@ -0,0 +1,151 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import org.bukkit.Location;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class AsyncTimedTeleport implements Runnable {
private static final double MOVE_CONSTANT = 0.3;
private final IUser teleportOwner;
private final IEssentials ess;
private final AsyncTeleport teleport;
private final UUID timer_teleportee;
private int timer_task;
private final long timer_started; // time this task was initiated
private final long timer_delay; // how long to delay the teleportPlayer
private double timer_health;
// note that I initially stored a clone of the location for reference, but...
// when comparing locations, I got incorrect mismatches (rounding errors, looked like)
// so, the X/Y/Z values are stored instead and rounded off
private final long timer_initX;
private final long timer_initY;
private final long timer_initZ;
private final ITarget timer_teleportTarget;
private final boolean timer_respawn;
private final boolean timer_canMove;
private final Trade timer_chargeFor;
private final TeleportCause timer_cause;
AsyncTimedTeleport(IUser user, IEssentials ess, AsyncTeleport teleport, long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
this.teleportOwner = user;
this.ess = ess;
this.teleport = teleport;
this.timer_started = System.currentTimeMillis();
this.timer_delay = delay;
this.timer_health = teleportUser.getBase().getHealth();
this.timer_initX = Math.round(teleportUser.getBase().getLocation().getX() * MOVE_CONSTANT);
this.timer_initY = Math.round(teleportUser.getBase().getLocation().getY() * MOVE_CONSTANT);
this.timer_initZ = Math.round(teleportUser.getBase().getLocation().getZ() * MOVE_CONSTANT);
this.timer_teleportee = teleportUser.getBase().getUniqueId();
this.timer_teleportTarget = target;
this.timer_chargeFor = chargeFor;
this.timer_cause = cause;
this.timer_respawn = respawn;
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
}
@Override
public void run() {
if (teleportOwner == null || !teleportOwner.getBase().isOnline() || teleportOwner.getBase().getLocation() == null) {
cancelTimer(false);
return;
}
final IUser teleportUser = ess.getUser(this.timer_teleportee);
if (teleportUser == null || !teleportUser.getBase().isOnline()) {
cancelTimer(false);
return;
}
final Location currLocation = teleportUser.getBase().getLocation();
if (currLocation == null) {
cancelTimer(false);
return;
}
if (!timer_canMove && (Math.round(currLocation.getX() * MOVE_CONSTANT) != timer_initX || Math.round(currLocation.getY() * MOVE_CONSTANT) != timer_initY || Math.round(currLocation.getZ() * MOVE_CONSTANT) != timer_initZ || teleportUser.getBase().getHealth() < timer_health)) {
// user moved, cancelTimer teleportPlayer
cancelTimer(true);
return;
}
class DelayedTeleportTask implements Runnable {
@Override
public void run() {
timer_health = teleportUser.getBase().getHealth(); // in case user healed, then later gets injured
final long now = System.currentTimeMillis();
if (now > timer_started + timer_delay) {
try {
teleport.cooldown(false);
} catch (Throwable ex) {
teleportOwner.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
if (teleportOwner != teleportUser) {
teleportUser.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
}
}
try {
cancelTimer(false);
teleportUser.sendMessage(tl("teleportationCommencing"));
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(e -> {
ess.showError(teleportOwner.getSource(), e, "\\ teleport");
return false;
});
if (timer_chargeFor != null) {
timer_chargeFor.isAffordableFor(teleportOwner);
}
if (timer_respawn) {
teleport.respawnNow(teleportUser, timer_cause, future);
} else {
teleport.nowAsync(teleportUser, timer_teleportTarget, timer_cause, future);
}
future.thenAccept(success -> {
if (timer_chargeFor != null) {
try {
timer_chargeFor.charge(teleportOwner);
} catch (ChargeException ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
}
}
});
} catch (Exception ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
}
}
}
}
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
}
//If we need to cancelTimer a pending teleportPlayer call this method
void cancelTimer(boolean notifyUser) {
if (timer_task == -1) {
return;
}
try {
ess.getServer().getScheduler().cancelTask(timer_task);
if (notifyUser) {
teleportOwner.sendMessage(tl("pendingTeleportCancelled"));
if (timer_teleportee != null && !timer_teleportee.equals(teleportOwner.getBase().getUniqueId())) {
ess.getUser(timer_teleportee).sendMessage(tl("pendingTeleportCancelled"));
}
}
} finally {
timer_task = -1;
}
}
}

View File

@ -1,5 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.api.IAsyncTeleport;
import com.earth2me.essentials.commands.IEssentialsCommand;
import net.ess3.api.ITeleport;
import net.ess3.api.MaxMoneyException;
@ -54,8 +55,14 @@ public interface IUser {
*/
boolean hasOutstandingTeleportRequest();
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.api.IAsyncTeleport IAsyncTeleport} with {@link IUser#getAsyncTeleport()}
*/
@Deprecated
ITeleport getTeleport();
IAsyncTeleport getAsyncTeleport();
BigDecimal getMoney();
void setMoney(final BigDecimal value) throws MaxMoneyException;

View File

@ -22,6 +22,7 @@ import org.bukkit.plugin.PluginManager;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -116,7 +117,11 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
}
}
/**
* @deprecated This method does not use asynchronous teleportation. Use {@link Jails#sendToJail(IUser, String, CompletableFuture)}
*/
@Override
@Deprecated
public void sendToJail(final IUser user, final String jail) throws Exception {
acquireReadLock();
try {
@ -130,6 +135,24 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
}
}
@Override
public void sendToJail(IUser user, String jail, CompletableFuture<Boolean> future) throws Exception {
acquireReadLock();
try {
if (user.getBase().isOnline()) {
Location loc = getJail(jail);
user.getAsyncTeleport().now(loc, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
user.setJail(jail);
});
return;
}
user.setJail(jail);
} finally {
unlock();
}
}
@Override
public void setJail(final String jailName, final Location loc) throws Exception {
acquireWriteLock();
@ -254,16 +277,22 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
return;
}
try {
sendToJail(user, user.getJail());
} catch (Exception ex) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(ex -> {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()));
}
return false;
});
future.thenAccept(success -> user.sendMessage(tl("jailMessage")));
try {
sendToJail(user, user.getJail(), future);
} catch (Exception ex) {
future.completeExceptionally(ex);
}
user.sendMessage(tl("jailMessage"));
}
}
}

View File

@ -21,6 +21,10 @@ import java.util.GregorianCalendar;
import static com.earth2me.essentials.I18n.tl;
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.AsyncTeleport AsyncTeleport}
*/
@Deprecated
public class Teleport implements ITeleport {
private final IUser teleportOwner;
private final IEssentials ess;
@ -28,6 +32,7 @@ public class Teleport implements ITeleport {
private TeleportType tpType;
@Deprecated
public Teleport(IUser user, IEssentials ess) {
this.teleportOwner = user;
this.ess = ess;
@ -40,6 +45,7 @@ public class Teleport implements ITeleport {
NORMAL
}
@Deprecated
public void cooldown(boolean check) throws Exception {
final Calendar time = new GregorianCalendar();
if (teleportOwner.getLastTeleportTimestamp() > 0) {
@ -73,6 +79,7 @@ public class Teleport implements ITeleport {
}
}
@Deprecated
private boolean cooldownApplies() {
boolean applies = true;
String globalBypassPerm = "essentials.teleport.cooldown.bypass";
@ -92,6 +99,7 @@ public class Teleport implements ITeleport {
return applies;
}
@Deprecated
private void warnUser(final IUser user, final double delay) {
Calendar c = new GregorianCalendar();
c.add(Calendar.SECOND, (int) delay);
@ -101,6 +109,7 @@ public class Teleport implements ITeleport {
//The now function is used when you want to skip tp delay when teleporting someone to a location or player.
@Override
@Deprecated
public void now(Location loc, boolean cooldown, TeleportCause cause) throws Exception {
if (cooldown) {
cooldown(false);
@ -110,6 +119,7 @@ public class Teleport implements ITeleport {
}
@Override
@Deprecated
public void now(Player entity, boolean cooldown, TeleportCause cause) throws Exception {
if (cooldown) {
cooldown(false);
@ -119,6 +129,7 @@ public class Teleport implements ITeleport {
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
@Deprecated
protected void now(IUser teleportee, ITarget target, TeleportCause cause) throws Exception {
cancel(false);
Location loc = target.getLocation();
@ -171,12 +182,14 @@ public class Teleport implements ITeleport {
}
@Override
@Deprecated
public void teleport(Location loc, Trade chargeFor, TeleportCause cause) throws Exception {
teleport(teleportOwner, new LocationTarget(loc), chargeFor, cause);
}
//This is used when teleporting to a player
@Override
@Deprecated
public void teleport(Player entity, Trade chargeFor, TeleportCause cause) throws Exception {
ITarget target = new PlayerTarget(entity);
teleportOwner.sendMessage(tl("teleportToPlayer", entity.getDisplayName()));
@ -185,12 +198,14 @@ public class Teleport implements ITeleport {
//This is used when teleporting to stored location
@Override
@Deprecated
public void teleportPlayer(IUser teleportee, Location loc, Trade chargeFor, TeleportCause cause) throws Exception {
teleport(teleportee, new LocationTarget(loc), chargeFor, cause);
}
//This is used on /tphere
@Override
@Deprecated
public void teleportPlayer(IUser teleportee, Player entity, Trade chargeFor, TeleportCause cause) throws Exception {
ITarget target = new PlayerTarget(entity);
teleport(teleportee, target, chargeFor, cause);
@ -198,6 +213,7 @@ public class Teleport implements ITeleport {
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
@Deprecated
private void teleport(IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
@ -228,6 +244,7 @@ public class Teleport implements ITeleport {
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
}
@Deprecated
private void teleportOther(IUser teleporter, IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
@ -263,6 +280,7 @@ public class Teleport implements ITeleport {
//The respawn function is a wrapper used to handle tp fallback, on /jail and /home
@Override
@Deprecated
public void respawn(final Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
if (chargeFor != null) {
@ -283,6 +301,7 @@ public class Teleport implements ITeleport {
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true);
}
@Deprecated
void respawnNow(IUser teleportee, TeleportCause cause) throws Exception {
final Player player = teleportee.getBase();
Location bed = player.getBedSpawnLocation();
@ -300,6 +319,7 @@ public class Teleport implements ITeleport {
//The warp function is a wrapper used to teleportPlayer a player to a /warp
@Override
@Deprecated
public void warp(IUser teleportee, String warp, Trade chargeFor, TeleportCause cause) throws Exception {
UserWarpEvent event = new UserWarpEvent(teleportee, warp, chargeFor);
Bukkit.getServer().getPluginManager().callEvent(event);
@ -318,12 +338,14 @@ public class Teleport implements ITeleport {
//The back function is a wrapper used to teleportPlayer a player /back to their previous location.
@Override
@Deprecated
public void back(Trade chargeFor) throws Exception {
back(teleportOwner, chargeFor);
}
//This function is a wrapper over the other back function for cases where another player performs back for them
@Override
@Deprecated
public void back(IUser teleporter, Trade chargeFor) throws Exception {
tpType = TeleportType.BACK;
final Location loc = teleportOwner.getLastLocation();
@ -333,15 +355,18 @@ public class Teleport implements ITeleport {
//This function is used to throw a user back after a jail sentence
@Override
@Deprecated
public void back() throws Exception {
now(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND);
}
@Deprecated
public void setTpType(TeleportType tpType) {
this.tpType = tpType;
}
//If we need to cancelTimer a pending teleportPlayer call this method
@Deprecated
private void cancel(boolean notifyUser) {
if (timedTeleport != null) {
timedTeleport.cancelTimer(notifyUser);
@ -349,6 +374,7 @@ public class Teleport implements ITeleport {
}
}
@Deprecated
private void initTimer(long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
timedTeleport = new TimedTeleport(teleportOwner, ess, this, delay, teleportUser, target, chargeFor, cause, respawn);
}

View File

@ -98,19 +98,17 @@ public class TimedTeleport implements Runnable {
cancelTimer(false);
teleportUser.sendMessage(tl("teleportationCommencing"));
try {
if (timer_chargeFor != null) {
timer_chargeFor.isAffordableFor(teleportOwner);
}
if (timer_respawn) {
teleport.respawnNow(teleportUser, timer_cause);
} else {
teleport.now(teleportUser, timer_teleportTarget, timer_cause);
}
if (timer_chargeFor != null) {
timer_chargeFor.charge(teleportOwner);
}
} catch (Exception ignored) {}
if (timer_chargeFor != null) {
timer_chargeFor.isAffordableFor(teleportOwner);
}
if (timer_respawn) {
teleport.respawnNow(teleportUser, timer_cause);
} else {
teleport.now(teleportUser, timer_teleportTarget, timer_cause);
}
if (timer_chargeFor != null) {
timer_chargeFor.charge(teleportOwner);
}
} catch (Exception ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");

View File

@ -18,6 +18,8 @@ import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -81,26 +83,43 @@ public class Trade {
}
public void isAffordableFor(final IUser user) throws ChargeException {
CompletableFuture<Boolean> future = new CompletableFuture<>();
isAffordableFor(user, future);
if (future.isCompletedExceptionally()) {
try {
future.get();
} catch (InterruptedException e) { //If this happens, we have bigger problems...
e.printStackTrace();
} catch (ExecutionException e) {
throw (ChargeException) e.getCause();
}
}
}
public void isAffordableFor(final IUser user, CompletableFuture<Boolean> future) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "checking if " + user.getName() + " can afford charge.");
}
if (getMoney() != null && getMoney().signum() > 0 && !user.canAfford(getMoney())) {
throw new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(getMoney(), ess)));
future.completeExceptionally(new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(getMoney(), ess))));
return;
}
if (getItemStack() != null && !user.getBase().getInventory().containsAtLeast(itemStack, itemStack.getAmount())) {
throw new ChargeException(tl("missingItems", getItemStack().getAmount(), ess.getItemDb().name(getItemStack())));
future.completeExceptionally(new ChargeException(tl("missingItems", getItemStack().getAmount(), ess.getItemDb().name(getItemStack()))));
return;
}
BigDecimal money;
if (command != null && !command.isEmpty() && (money = getCommandCost(user)).signum() > 0 && !user.canAfford(money)) {
throw new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(money, ess)));
future.completeExceptionally(new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(money, ess))));
return;
}
if (exp != null && exp > 0 && SetExpFix.getTotalExperience(user.getBase()) < exp) {
throw new ChargeException(tl("notEnoughExperience"));
future.completeExceptionally(new ChargeException(tl("notEnoughExperience")));
}
}
@ -175,6 +194,20 @@ public class Trade {
}
public void charge(final IUser user) throws ChargeException {
CompletableFuture<Boolean> future = new CompletableFuture<>();
charge(user, future);
if (future.isCompletedExceptionally()) {
try {
future.get();
} catch (InterruptedException e) { //If this happens, we have bigger problems...
e.printStackTrace();
} catch (ExecutionException e) {
throw (ChargeException) e.getCause();
}
}
}
public void charge(final IUser user, CompletableFuture<Boolean> future) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "attempting to charge user " + user.getName());
}
@ -183,7 +216,8 @@ public class Trade {
ess.getLogger().log(Level.INFO, "charging user " + user.getName() + " money " + getMoney().toPlainString());
}
if (!user.canAfford(getMoney()) && getMoney().signum() > 0) {
throw new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(getMoney(), ess)));
future.completeExceptionally(new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(getMoney(), ess))));
return;
}
user.takeMoney(getMoney());
}
@ -192,7 +226,8 @@ public class Trade {
ess.getLogger().log(Level.INFO, "charging user " + user.getName() + " itemstack " + getItemStack().toString());
}
if (!user.getBase().getInventory().containsAtLeast(getItemStack(), getItemStack().getAmount())) {
throw new ChargeException(tl("missingItems", getItemStack().getAmount(), getItemStack().getType().toString().toLowerCase(Locale.ENGLISH).replace("_", " ")));
future.completeExceptionally(new ChargeException(tl("missingItems", getItemStack().getAmount(), getItemStack().getType().toString().toLowerCase(Locale.ENGLISH).replace("_", " "))));
return;
}
user.getBase().getInventory().removeItem(getItemStack());
user.getBase().updateInventory();
@ -200,7 +235,8 @@ public class Trade {
if (command != null) {
final BigDecimal cost = getCommandCost(user);
if (!user.canAfford(cost) && cost.signum() > 0) {
throw new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(cost, ess)));
future.completeExceptionally(new ChargeException(tl("notEnoughMoney", NumberUtil.displayCurrency(cost, ess))));
return;
}
user.takeMoney(cost);
}
@ -210,7 +246,8 @@ public class Trade {
}
final int experience = SetExpFix.getTotalExperience(user.getBase());
if (experience < getExperience() && getExperience() > 0) {
throw new ChargeException(tl("notEnoughExperience"));
future.completeExceptionally(new ChargeException(tl("notEnoughExperience")));
return;
}
SetExpFix.setTotalExperience(user.getBase(), experience - getExperience());
}

View File

@ -16,7 +16,6 @@ import net.ess3.api.events.JailStatusChangeEvent;
import net.ess3.api.events.MuteStatusChangeEvent;
import net.ess3.api.events.UserBalanceUpdateEvent;
import net.ess3.nms.refl.ReflUtil;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
@ -29,6 +28,7 @@ import org.bukkit.potion.PotionEffectType;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -42,7 +42,8 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
private transient boolean teleportRequestHere;
private transient Location teleportLocation;
private transient boolean vanished;
private transient final Teleport teleport;
private transient final AsyncTeleport teleport;
private transient final Teleport legacyTeleport;
private transient long teleportRequestTime;
private transient long lastOnlineActivity;
private transient long lastThrottledAction;
@ -63,7 +64,8 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
public User(final Player base, final IEssentials ess) {
super(base, ess);
teleport = new Teleport(this, ess);
teleport = new AsyncTeleport(this, ess);
legacyTeleport = new Teleport(this, ess);
if (isAfk()) {
afkPosition = this.getLocation();
}
@ -401,10 +403,19 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
}
@Override
public Teleport getTeleport() {
public AsyncTeleport getAsyncTeleport() {
return teleport;
}
/**
* @deprecated This API is not asynchronous. Use {@link User#getAsyncTeleport()}
*/
@Override
@Deprecated
public Teleport getTeleport() {
return legacyTeleport;
}
public long getLastOnlineActivity() {
return lastOnlineActivity;
}
@ -567,14 +578,12 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
sendMessage(tl("haveBeenReleased"));
setJail(null);
if (ess.getSettings().isTeleportBackWhenFreedFromJail()) {
try {
getTeleport().back();
} catch (Exception ex) {
try {
getTeleport().respawn(null, TeleportCause.PLUGIN);
} catch (Exception ignored) {
}
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
getAsyncTeleport().back(future);
future.exceptionally(e -> {
getAsyncTeleport().respawn(null, TeleportCause.PLUGIN, new CompletableFuture<>());
return false;
});
}
return true;
}

View File

@ -0,0 +1,123 @@
package com.earth2me.essentials.api;
import com.earth2me.essentials.Trade;
import net.ess3.api.IUser;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.concurrent.CompletableFuture;
public interface IAsyncTeleport {
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param loc - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Location loc, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param entity - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Player entity, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause.
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param otherUser - Which user will be teleported
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param otherUser - Which user will be teleported
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle tp fallback on /jail and /home
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void respawn(final Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /warp teleports
*
* @param otherUser - Which user will be teleported
* @param warp - The name of the warp the user will be teleported too.
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void warp(IUser otherUser, String warp, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports that
* are executed by a different player with this
* instance of teleport as a target.
*
* @param teleporter - The user performing the /back command.
* This value may be {@code null} to indicate console.
* @param chargeFor - What the {@code teleporter} will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(IUser teleporter, Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle throwing user home after a jail sentence
*
* @param future - Future which is completed with the success status of the execution
*/
void back(CompletableFuture<Boolean> future);
}

View File

@ -4,6 +4,7 @@ import net.ess3.api.IUser;
import org.bukkit.Location;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public interface IJails extends IReload {
@ -46,13 +47,27 @@ public interface IJails extends IReload {
/**
* Attempts to send the given user to the given jail
*
* @deprecated Use {@link IJails#sendToJail(IUser, String, CompletableFuture)}
*
* @param user the user to send to jail
* @param jail the jail to send the user to
*
* @throws Exception if the user is offline or jail does not exist
*/
@Deprecated
void sendToJail(IUser user, String jail) throws Exception;
/**
* Attempts to send the given user to the given jail
*
* @param user the user to send to jail
* @param jail the jail to send the user to
* @param future Future which is completed with the success status of the execution
*
* @throws Exception if the user is offline or jail does not exist
*/
void sendToJail(IUser user, String jail, CompletableFuture<Boolean> future) throws Exception;
/**
* Set a new jail with the given name and location
*

View File

@ -7,6 +7,9 @@ import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.api.IAsyncTeleport IAsyncTeleport}
*/
public interface ITeleport {
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
@ -17,6 +20,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void now(Location loc, boolean cooldown, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -28,6 +32,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void now(Player entity, boolean cooldown, PlayerTeleportEvent.TeleportCause cause) throws Exception;
@Deprecated
@ -42,6 +47,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void teleport(Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -53,6 +59,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void teleport(Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -65,6 +72,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -77,6 +85,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -87,6 +96,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void respawn(final Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -99,6 +109,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void warp(IUser otherUser, String warp, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause) throws Exception;
/**
@ -108,6 +119,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void back(Trade chargeFor) throws Exception;
/**
@ -117,10 +129,11 @@ public interface ITeleport {
*
* @param teleporter - The user performing the /back command.
* This value may be {@code null} to indicate console.
* @param chargeFor - What the {@code teleporter} will be charged if teleportPlayer is successful
* @param chargeFor - What the {@code teleporter} will be charged if teleportPlayer is successful
*
* @throws Exception
*/
@Deprecated
void back(IUser teleporter, Trade chargeFor) throws Exception;
/**
@ -128,6 +141,7 @@ public interface ITeleport {
*
* @throws Exception
*/
@Deprecated
void back() throws Exception;
}

View File

@ -23,11 +23,11 @@ public class Commandback extends EssentialsCommand {
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
CommandSource sender = user.getSource();
if (args.length > 0 && user.isAuthorized("essentials.back.others")) {
this.parseCommand(server, sender, args, true);
this.parseCommand(server, sender, args, true, commandLabel);
return;
}
teleportBack(sender, user);
teleportBack(sender, user, commandLabel);
}
@Override
@ -36,10 +36,10 @@ public class Commandback extends EssentialsCommand {
throw new NotEnoughArgumentsException();
}
this.parseCommand(server, sender, args, true);
this.parseCommand(server, sender, args, true, commandLabel);
}
private void parseCommand(Server server, CommandSource sender, String[] args, boolean allowOthers) throws Exception {
private void parseCommand(Server server, CommandSource sender, String[] args, boolean allowOthers, String commandLabel) throws Exception {
Collection<Player> players = new ArrayList<>();
if (allowOthers && args.length > 0 && args[0].trim().length() > 2) {
@ -52,11 +52,11 @@ public class Commandback extends EssentialsCommand {
for (Player player : players) {
sender.sendMessage(tl("backOther", player.getName()));
teleportBack(sender, ess.getUser(player));
teleportBack(sender, ess.getUser(player), commandLabel);
}
}
private void teleportBack(CommandSource sender, User user) throws Exception {
private void teleportBack(CommandSource sender, User user, String commandLabel) throws Exception {
if (user.getLastLocation() == null) {
throw new Exception(tl("noLocationFound"));
}
@ -77,15 +77,15 @@ public class Commandback extends EssentialsCommand {
}
if (requester == null) {
user.getTeleport().back(null, null);
user.getAsyncTeleport().back(null, null, getNewExceptionFuture(sender, commandLabel));
} else if (!requester.equals(user)) {
Trade charge = new Trade(this.getName(), this.ess);
charge.isAffordableFor(requester);
user.getTeleport().back(requester, charge);
user.getAsyncTeleport().back(requester, charge, getNewExceptionFuture(sender, commandLabel));
} else {
Trade charge = new Trade(this.getName(), this.ess);
charge.isAffordableFor(user);
user.getTeleport().back(charge);
user.getAsyncTeleport().back(charge, getNewExceptionFuture(sender, commandLabel));
}
throw new NoChargeException();
}

View File

@ -3,6 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.StringUtil;
import io.papermc.lib.PaperLib;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
@ -10,6 +11,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -39,41 +41,56 @@ public class Commandhome extends EssentialsCommand {
}
try {
if ("bed".equalsIgnoreCase(homeName) && user.isAuthorized("essentials.home.bed")) {
final Location bed = player.getBase().getBedSpawnLocation();
if (bed != null) {
user.getTeleport().teleport(bed, charge, TeleportCause.COMMAND);
throw new NoChargeException();
} else {
throw new Exception(tl("bedMissing"));
if (!player.getBase().isOnline()) {
throw new Exception(tl("bedOffline"));
}
}
goHome(user, player, homeName.toLowerCase(Locale.ENGLISH), charge);
} catch (NotEnoughArgumentsException e) {
Location bed = player.getBase().getBedSpawnLocation();
final List<String> homes = player.getHomes();
if (homes.isEmpty() && player.equals(user)) {
if (ess.getSettings().isSpawnIfNoHome()) {
user.getTeleport().respawn(charge, TeleportCause.COMMAND);
} else {
throw new Exception(tl("noHomeSetPlayer"));
}
} else if (homes.isEmpty()) {
throw new Exception(tl("noHomeSetPlayer"));
} else if (homes.size() == 1 && player.equals(user)) {
goHome(user, player, homes.get(0), charge);
} else {
final int count = homes.size();
if (user.isAuthorized("essentials.home.bed")) {
if (bed != null) {
homes.add(tl("bed"));
PaperLib.getBedSpawnLocationAsync(player.getBase(), true).thenAccept(location -> {
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
if (location != null) {
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleportHome", "bed"));
}
});
user.getAsyncTeleport().teleport(location, charge, TeleportCause.COMMAND, future);
} else {
homes.add(tl("bedNull"));
showError(user.getBase(), new Exception(tl("bedMissing")), commandLabel);
}
}
user.sendMessage(tl("homes", StringUtil.joinList(homes), count, getHomeLimit(player)));
});
return;
}
goHome(user, player, homeName.toLowerCase(Locale.ENGLISH), charge, getNewExceptionFuture(user.getSource(), commandLabel));
} catch (NotEnoughArgumentsException e) {
final User finalPlayer = player;
PaperLib.getBedSpawnLocationAsync(player.getBase(), true).thenAccept(bed -> {
final List<String> homes = finalPlayer.getHomes();
if (homes.isEmpty() && finalPlayer.equals(user)) {
if (ess.getSettings().isSpawnIfNoHome()) {
user.getAsyncTeleport().respawn(charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
} else {
showError(user.getBase(), new Exception(tl("noHomeSetPlayer")), commandLabel);
}
} else if (homes.isEmpty()) {
showError(user.getBase(), new Exception(tl("noHomeSetPlayer")), commandLabel);
} else if (homes.size() == 1 && finalPlayer.equals(user)) {
try {
goHome(user, finalPlayer, homes.get(0), charge, getNewExceptionFuture(user.getSource(), commandLabel));
} catch (Exception exception) {
showError(user.getBase(), exception, commandLabel);
}
} else {
final int count = homes.size();
if (user.isAuthorized("essentials.home.bed")) {
if (bed != null) {
homes.add(tl("bed"));
} else {
homes.add(tl("bedNull"));
}
}
user.sendMessage(tl("homes", StringUtil.joinList(homes), count, getHomeLimit(finalPlayer)));
}
});
}
throw new NoChargeException();
}
private String getHomeLimit(final User player) {
@ -86,7 +103,7 @@ public class Commandhome extends EssentialsCommand {
return Integer.toString(ess.getSettings().getHomeLimit(player));
}
private void goHome(final User user, final User player, final String home, final Trade charge) throws Exception {
private void goHome(final User user, final User player, final String home, final Trade charge, CompletableFuture<Boolean> future) throws Exception {
if (home.length() < 1) {
throw new NotEnoughArgumentsException();
}
@ -97,8 +114,12 @@ public class Commandhome extends EssentialsCommand {
if (user.getWorld() != loc.getWorld() && ess.getSettings().isWorldHomePermissions() && !user.isAuthorized("essentials.worlds." + loc.getWorld().getName())) {
throw new Exception(tl("noPerm", "essentials.worlds." + loc.getWorld().getName()));
}
user.getTeleport().teleport(loc, charge, TeleportCause.COMMAND);
user.sendMessage(tl("teleportHome", home));
user.getAsyncTeleport().teleport(loc, charge, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleportHome", home));
}
});
}
@Override

View File

@ -46,8 +46,7 @@ public class Commandjump extends EssentialsCommand {
final Trade charge = new Trade(this.getName(), ess);
charge.isAffordableFor(user);
user.getTeleport().teleport(loc, charge, TeleportCause.COMMAND);
throw new NoChargeException();
user.getAsyncTeleport().teleport(loc, charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override

View File

@ -9,6 +9,7 @@ import org.bukkit.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -43,23 +44,33 @@ public class Commandtogglejail extends EssentialsCommand {
ess.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
long preTimeDiff = 0;
if (args.length > 2) {
final String time = getFinalArg(args, 2);
preTimeDiff = DateUtil.parseDateDiff(time, true);
}
final long timeDiff = preTimeDiff;
CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
future.thenAccept(success -> {
if (success) {
player.setJailed(true);
player.sendMessage(tl("userJailed"));
player.setJail(null);
player.setJail(args[1]);
if (args.length > 2) {
player.setJailTimeout(timeDiff);
}
sender.sendMessage((timeDiff > 0 ? tl("playerJailedFor", player.getName(), DateUtil.formatDateDiff(timeDiff)) : tl("playerJailed", player.getName())));
}
});
if (player.getBase().isOnline()) {
ess.getJails().sendToJail(player, args[1]);
ess.getJails().sendToJail(player, args[1], future);
} else {
// Check if jail exists
ess.getJails().getJail(args[1]);
future.complete(true);
}
player.setJailed(true);
player.sendMessage(tl("userJailed"));
player.setJail(null);
player.setJail(args[1]);
long timeDiff = 0;
if (args.length > 2) {
final String time = getFinalArg(args, 2);
timeDiff = DateUtil.parseDateDiff(time, true);
player.setJailTimeout(timeDiff);
}
sender.sendMessage((timeDiff > 0 ? tl("playerJailedFor", player.getName(), DateUtil.formatDateDiff(timeDiff)) : tl("playerJailed", player.getName())));
}
return;
}
@ -91,7 +102,14 @@ public class Commandtogglejail extends EssentialsCommand {
player.sendMessage(tl("jailReleasedPlayerNotify"));
player.setJail(null);
if (player.getBase().isOnline()) {
player.getTeleport().back();
CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
player.getAsyncTeleport().back(future);
future.thenAccept(success -> {
if (success) {
sender.sendMessage(tl("jailReleased", player.getName()));
}
});
return;
}
sender.sendMessage(tl("jailReleased", player.getName()));
}

View File

@ -7,6 +7,8 @@ import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -22,8 +24,12 @@ public class Commandtop extends EssentialsCommand {
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));
user.getTeleport().teleport(loc, new Trade(this.getName(), ess), TeleportCause.COMMAND);
user.sendMessage(tl("teleportTop", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleportTop", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
});
user.getAsyncTeleport().teleport(loc, new Trade(this.getName(), ess), TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
}

View File

@ -10,6 +10,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -21,6 +22,7 @@ public class Commandtp extends EssentialsCommand {
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
switch (args.length) {
case 0:
throw new NotEnoughArgumentsException();
@ -41,8 +43,8 @@ public class Commandtp extends EssentialsCommand {
}
final Trade charge = new Trade(this.getName(), ess);
charge.isAffordableFor(user);
user.getTeleport().teleport(player.getBase(), charge, TeleportCause.COMMAND);
throw new NoChargeException();
user.getAsyncTeleport().teleport(player.getBase(), charge, TeleportCause.COMMAND, future);
break;
case 3:
if (!user.isAuthorized("essentials.tp.position")) {
throw new Exception(tl("noPerm", "essentials.tp.position"));
@ -54,8 +56,12 @@ public class Commandtp extends EssentialsCommand {
throw new NotEnoughArgumentsException(tl("teleportInvalidLocation"));
}
final Location locpos = new Location(user.getWorld(), x2, y2, z2, user.getLocation().getYaw(), user.getLocation().getPitch());
user.getTeleport().now(locpos, false, TeleportCause.COMMAND);
user.sendMessage(tl("teleporting", locpos.getWorld().getName(), locpos.getBlockX(), locpos.getBlockY(), locpos.getBlockZ()));
user.getAsyncTeleport().now(locpos, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleporting", locpos.getWorld().getName(), locpos.getBlockX(), locpos.getBlockY(), locpos.getBlockZ()));
}
});
break;
case 4:
if (!user.isAuthorized("essentials.tp.others")) {
@ -76,8 +82,12 @@ public class Commandtp extends EssentialsCommand {
throw new Exception(tl("teleportDisabled", target2.getDisplayName()));
}
user.sendMessage(tl("teleporting", locposother.getWorld().getName(), locposother.getBlockX(), locposother.getBlockY(), locposother.getBlockZ()));
target2.getTeleport().now(locposother, false, TeleportCause.COMMAND);
target2.sendMessage(tl("teleporting", locposother.getWorld().getName(), locposother.getBlockX(), locposother.getBlockY(), locposother.getBlockZ()));
target2.getAsyncTeleport().now(locposother, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
target2.sendMessage(tl("teleporting", locposother.getWorld().getName(), locposother.getBlockX(), locposother.getBlockY(), locposother.getBlockZ()));
}
});
break;
case 2:
default:
@ -96,7 +106,7 @@ public class Commandtp extends EssentialsCommand {
throw new Exception(tl("noPerm", "essentials.worlds." + toPlayer.getWorld().getName()));
}
target.sendMessage(tl("teleportAtoB", user.getDisplayName(), toPlayer.getDisplayName()));
target.getTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND);
target.getAsyncTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND, future);
break;
}
}
@ -111,7 +121,7 @@ public class Commandtp extends EssentialsCommand {
if (args.length == 2) {
final User toPlayer = getPlayer(server, args, 1, true, false);
target.sendMessage(tl("teleportAtoB", Console.NAME, toPlayer.getDisplayName()));
target.getTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND);
target.getAsyncTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND, getNewExceptionFuture(sender, commandLabel));
} else if (args.length > 3) {
final double x = args[1].startsWith("~") ? target.getLocation().getX() + (args[1].length() > 1 ? Double.parseDouble(args[1].substring(1)) : 0) : Double.parseDouble(args[1]);
final double y = args[2].startsWith("~") ? target.getLocation().getY() + (args[2].length() > 1 ? Double.parseDouble(args[2].substring(1)) : 0) : Double.parseDouble(args[2]);
@ -121,8 +131,13 @@ public class Commandtp extends EssentialsCommand {
}
final Location loc = new Location(target.getWorld(), x, y, z, target.getLocation().getYaw(), target.getLocation().getPitch());
sender.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
target.getTeleport().now(loc, false, TeleportCause.COMMAND);
target.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
target.getAsyncTeleport().now(loc, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
target.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
});
} else {
throw new NotEnoughArgumentsException();
}

View File

@ -1,6 +1,6 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.Teleport;
import com.earth2me.essentials.AsyncTeleport;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import net.ess3.api.events.TPARequestEvent;
@ -9,6 +9,7 @@ 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;
@ -44,11 +45,16 @@ public class Commandtpa extends EssentialsCommand {
}
if (player.isAutoTeleportEnabled() && !player.isIgnoredPlayer(user)) {
final Trade charge = new Trade(this.getName(), ess);
Teleport teleport = user.getTeleport();
teleport.setTpType(Teleport.TeleportType.TPA);
teleport.teleport(player.getBase(), charge, PlayerTeleportEvent.TeleportCause.COMMAND);
player.sendMessage(tl("requestAcceptedAuto", user.getDisplayName()));
user.sendMessage(tl("requestAcceptedFromAuto", player.getDisplayName()));
AsyncTeleport teleport = (AsyncTeleport) user.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
teleport.teleport(player.getBase(), charge, PlayerTeleportEvent.TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
player.sendMessage(tl("requestAcceptedAuto", user.getDisplayName()));
user.sendMessage(tl("requestAcceptedFromAuto", player.getDisplayName()));
}
});
return;
}

View File

@ -1,12 +1,14 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.Teleport;
import com.earth2me.essentials.AsyncTeleport;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -49,24 +51,31 @@ public class Commandtpaccept extends EssentialsCommand {
user.sendMessage(tl("requestAccepted"));
requester.sendMessage(tl("requestAcceptedFrom", user.getDisplayName()));
try {
if (user.isTpRequestHere()) {
final Location loc = user.getTpRequestLocation();
Teleport teleport = requester.getTeleport();
teleport.setTpType(Teleport.TeleportType.TPA);
teleport.teleportPlayer(user, user.getTpRequestLocation(), charge, TeleportCause.COMMAND);
requester.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
} else {
Teleport teleport = requester.getTeleport();
teleport.setTpType(Teleport.TeleportType.TPA);
teleport.teleport(user.getBase(), charge, TeleportCause.COMMAND);
}
} catch (Exception ex) {
CompletableFuture<Boolean> future = getNewExceptionFuture(requester.getSource(), commandLabel);
future.exceptionally(e -> {
user.sendMessage(tl("pendingTeleportCancelled"));
ess.showError(requester.getSource(), ex, commandLabel);
return false;
});
future.thenAccept(success -> {
if (success) {
user.requestTeleport(null, false);
}
});
if (user.isTpRequestHere()) {
final Location loc = user.getTpRequestLocation();
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
future.thenAccept(success -> {
if (success) {
requester.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
});
teleport.teleportPlayer(user, user.getTpRequestLocation(), charge, TeleportCause.COMMAND, future);
} else {
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
teleport.teleport(user.getBase(), charge, TeleportCause.COMMAND, future);
}
user.requestTeleport(null, false);
throw new NoChargeException();
}
}

View File

@ -21,17 +21,17 @@ public class Commandtpall extends EssentialsCommand {
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
if (sender.isPlayer()) {
teleportAllPlayers(server, sender, ess.getUser(sender.getPlayer()));
teleportAllPlayers(server, sender, ess.getUser(sender.getPlayer()), commandLabel);
return;
}
throw new NotEnoughArgumentsException();
}
final User target = getPlayer(server, sender, args, 0);
teleportAllPlayers(server, sender, target);
teleportAllPlayers(server, sender, target, commandLabel);
}
private void teleportAllPlayers(Server server, CommandSource sender, User target) {
private void teleportAllPlayers(Server server, CommandSource sender, User target, String label) {
sender.sendMessage(tl("teleportAll"));
final Location loc = target.getLocation();
for (User player : ess.getOnlineUsers()) {
@ -41,11 +41,7 @@ public class Commandtpall extends EssentialsCommand {
if (sender.getSender().equals(target.getBase()) && target.getWorld() != player.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !target.isAuthorized("essentials.worlds." + target.getWorld().getName())) {
continue;
}
try {
player.getTeleport().now(loc, false, TeleportCause.COMMAND);
} catch (Exception ex) {
ess.showError(sender, ex, getName());
}
player.getAsyncTeleport().now(loc, false, TeleportCause.COMMAND, getNewExceptionFuture(sender, label));
}
}

View File

@ -25,8 +25,7 @@ public class Commandtphere extends EssentialsCommand {
if (user.getWorld() != player.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + user.getWorld().getName())) {
throw new Exception(tl("noPerm", "essentials.worlds." + user.getWorld().getName()));
}
user.getTeleport().teleportPlayer(player, user.getBase(), new Trade(this.getName(), ess), TeleportCause.COMMAND);
throw new NoChargeException();
user.getAsyncTeleport().teleportPlayer(player, user.getBase(), new Trade(this.getName(), ess), TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override

View File

@ -6,6 +6,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -26,7 +27,7 @@ public class Commandtpo extends EssentialsCommand {
if (user.getWorld() != player.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + player.getWorld().getName())) {
throw new Exception(tl("noPerm", "essentials.worlds." + player.getWorld().getName()));
}
user.getTeleport().now(player.getBase(), false, TeleportCause.COMMAND);
user.getAsyncTeleport().now(player.getBase(), false, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
break;
default:
@ -40,8 +41,13 @@ public class Commandtpo extends EssentialsCommand {
throw new Exception(tl("noPerm", "essentials.worlds." + toPlayer.getWorld().getName()));
}
target.getTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND);
target.sendMessage(tl("teleportAtoB", user.getDisplayName(), toPlayer.getDisplayName()));
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
target.getAsyncTeleport().now(toPlayer.getBase(), false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
target.sendMessage(tl("teleportAtoB", user.getDisplayName(), toPlayer.getDisplayName()));
}
});
break;
}
}

View File

@ -28,7 +28,7 @@ public class Commandtpoffline extends EssentialsCommand {
}
user.sendMessage(tl("teleporting", logout.getWorld().getName(), logout.getBlockX(), logout.getBlockY(), logout.getBlockZ()));
user.getTeleport().now(logout, false, PlayerTeleportEvent.TeleportCause.COMMAND);
user.getAsyncTeleport().now(logout, false, PlayerTeleportEvent.TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), label));
}
}
}

View File

@ -28,8 +28,7 @@ public class Commandtpohere extends EssentialsCommand {
throw new Exception(tl("noPerm", "essentials.worlds." + user.getWorld().getName()));
}
// Verify permission
player.getTeleport().now(user.getBase(), false, TeleportCause.COMMAND);
player.getAsyncTeleport().now(user.getBase(), false, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override

View File

@ -55,8 +55,7 @@ public class Commandtppos extends EssentialsCommand {
final Trade charge = new Trade(this.getName(), ess);
charge.isAffordableFor(user);
user.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
user.getTeleport().teleport(loc, charge, TeleportCause.COMMAND);
throw new NoChargeException();
user.getAsyncTeleport().teleport(loc, charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override
@ -85,8 +84,7 @@ public class Commandtppos extends EssentialsCommand {
}
sender.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
user.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
user.getTeleport().teleport(loc, null, TeleportCause.COMMAND);
user.getAsyncTeleport().teleport(loc, null, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override

View File

@ -41,11 +41,10 @@ public class Commandwarp extends EssentialsCommand {
User otherUser = null;
if (args.length == 2 && (user.isAuthorized("essentials.warp.otherplayers") || user.isAuthorized("essentials.warp.others"))) {
otherUser = getPlayer(server, user, args, 1);
warpUser(user, otherUser, args[0]);
throw new NoChargeException();
warpUser(user, otherUser, args[0], commandLabel);
return;
}
warpUser(user, user, args[0]);
throw new NoChargeException();
warpUser(user, user, args[0], commandLabel);
}
}
@ -56,9 +55,7 @@ public class Commandwarp extends EssentialsCommand {
throw new NoChargeException();
}
User otherUser = getPlayer(server, args, 1, true, false);
otherUser.getTeleport().warp(otherUser, args[0], null, TeleportCause.COMMAND);
throw new NoChargeException();
otherUser.getAsyncTeleport().warp(otherUser, args[0], null, TeleportCause.COMMAND, getNewExceptionFuture(sender, commandLabel));
}
//TODO: Use one of the new text classes, like /help ?
@ -91,7 +88,7 @@ public class Commandwarp extends EssentialsCommand {
}
}
private void warpUser(final User owner, final User user, final String name) throws Exception {
private void warpUser(final User owner, final User user, final String name, final String commandLabel) throws Exception {
final Trade chargeWarp = new Trade("warp-" + name.toLowerCase(Locale.ENGLISH).replace('_', '-'), ess);
final Trade chargeCmd = new Trade(this.getName(), ess);
final BigDecimal fullCharge = chargeWarp.getCommandCost(user).add(chargeCmd.getCommandCost(user));
@ -100,7 +97,7 @@ public class Commandwarp extends EssentialsCommand {
if (ess.getSettings().getPerWarpPermission() && !owner.isAuthorized("essentials.warps." + name)) {
throw new Exception(tl("warpUsePermission"));
}
owner.getTeleport().warp(user, name, charge, TeleportCause.COMMAND);
owner.getAsyncTeleport().warp(user, name, charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
private List<String> getAvailableWarpsFor(final IUser user) {

View File

@ -66,8 +66,7 @@ public class Commandworld extends EssentialsCommand {
final Trade charge = new Trade(this.getName(), ess);
charge.isAffordableFor(user);
user.getTeleport().teleport(target, charge, TeleportCause.COMMAND);
throw new NoChargeException();
user.getAsyncTeleport().teleport(target, charge, TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), commandLabel));
}
@Override

View File

@ -1,5 +1,6 @@
package com.earth2me.essentials.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import com.earth2me.essentials.CommandSource;
@ -24,6 +25,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
@ -346,6 +349,24 @@ public abstract class EssentialsCommand implements IEssentialsCommand {
return command.tabComplete(sender.getSender(), label, effectiveArgs);
}
@Override
public void showError(CommandSender sender, Throwable throwable, String commandLabel) {
sender.sendMessage(tl("errorWithMessage", throwable.getMessage()));
if (ess.getSettings().isDebug()) {
logger.log(Level.INFO, tl("errorCallingCommand", commandLabel), throwable);
throwable.printStackTrace();
}
}
public CompletableFuture<Boolean> getNewExceptionFuture(CommandSource sender, String commandLabel) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(e -> {
showError(sender.getSender(), e, commandLabel);
return false;
});
return future;
}
/**
* Common time durations (in seconds), for use in tab completion.
*/

View File

@ -6,6 +6,8 @@ import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.List;
@ -23,4 +25,6 @@ public interface IEssentialsCommand {
void setEssentials(IEssentials ess);
void setEssentialsModule(IEssentialsModule module);
void showError(CommandSender sender, Throwable throwable, String commandLabel);
}

View File

@ -6,6 +6,8 @@ import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -52,12 +54,17 @@ public class SignWarp extends EssentialsSign {
}
final Trade charge = getTrade(sign, 3, ess);
try {
player.getTeleport().warp(player, warpName, charge, TeleportCause.PLUGIN);
Trade.log("Sign", "Warp", "Interact", username, null, username, charge, sign.getBlock().getLocation(), ess);
} catch (Exception ex) {
throw new SignException(ex.getMessage(), ex);
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
player.getAsyncTeleport().warp(player, warpName, charge, TeleportCause.PLUGIN, future);
future.thenAccept(success -> {
if (success) {
Trade.log("Sign", "Warp", "Interact", username, null, username, charge, sign.getBlock().getLocation(), ess);
}
});
future.exceptionally(e -> {
ess.showError(player.getSource(), e, "\\ sign: " + signName);
return false;
});
return true;
}
}

View File

@ -153,7 +153,7 @@ public class LocationUtil {
}
public static Location getSafeDestination(final IEssentials ess, final IUser user, final Location loc) throws Exception {
if (user.getBase().isOnline() && loc.getWorld().equals(user.getBase().getWorld()) && (user.getBase().getGameMode() == GameMode.CREATIVE || user.isGodModeEnabled()) && user.getBase().getAllowFlight()) {
if (user.getBase().isOnline() && (user.getBase().getGameMode() == GameMode.CREATIVE || user.getBase().getGameMode() == GameMode.SPECTATOR || user.isGodModeEnabled()) || user.getBase().getAllowFlight()) {
if (shouldFly(loc)) {
user.getBase().setFlying(true);
}

View File

@ -56,6 +56,7 @@ banipCommandUsage=/<command> <address>
bed=\u00a7obed\u00a7r
bedMissing=\u00a74Your bed is either unset, missing or blocked.
bedNull=\u00a7mbed\u00a7r
bedOffline=\u00a74Cannot teleport to the beds of offline users.
bedSet=\u00a76Bed spawn set\!
beezookaCommandDescription=Throw an exploding bee at your opponent.
beezookaCommandUsage=/<command>

View File

@ -1,6 +1,9 @@
package net.ess3.api;
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.api.IAsyncTeleport IAsyncTeleport}
*/
public interface ITeleport extends com.earth2me.essentials.api.ITeleport {
}

View File

@ -1,11 +1,12 @@
package net.ess3.api.events;
import net.ess3.api.IUser;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
/**
* Called when the player teleports
*/
@ -18,6 +19,7 @@ public class UserTeleportEvent extends Event implements Cancellable {
private boolean cancelled = false;
public UserTeleportEvent(IUser user, TeleportCause cause, Location target) {
super(!Bukkit.isPrimaryThread());
this.user = user;
this.cause = cause;
this.target = target;

View File

@ -5,12 +5,13 @@ import com.earth2me.essentials.Console;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.commands.EssentialsCommand;
import com.earth2me.essentials.commands.NoChargeException;
import com.earth2me.essentials.commands.NotEnoughArgumentsException;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
@ -25,14 +26,18 @@ public class Commandspawn extends EssentialsCommand {
charge.isAffordableFor(user);
if (args.length > 0 && user.isAuthorized("essentials.spawn.others")) {
final User otherUser = getPlayer(server, user, args, 0);
respawn(user.getSource(), user, otherUser, charge);
if (!otherUser.equals(user)) {
otherUser.sendMessage(tl("teleportAtoB", user.getDisplayName(), "spawn"));
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.thenAccept(success -> {
if (success) {
if (!otherUser.equals(user)) {
otherUser.sendMessage(tl("teleportAtoB", user.getDisplayName(), "spawn"));
}
}
});
respawn(user.getSource(), user, otherUser, charge, commandLabel, future);
} else {
respawn(user.getSource(), user, user, charge);
respawn(user.getSource(), user, user, charge, commandLabel, new CompletableFuture<>());
}
throw new NoChargeException();
}
@Override
@ -41,19 +46,27 @@ public class Commandspawn extends EssentialsCommand {
throw new NotEnoughArgumentsException();
}
final User user = getPlayer(server, args, 0, true, false);
respawn(sender, null, user, null);
user.sendMessage(tl("teleportAtoB", Console.NAME, "spawn"));
CompletableFuture<Boolean> future = new CompletableFuture<>();
respawn(sender, null, user, null, commandLabel, future);
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("teleportAtoB", Console.NAME, "spawn"));
}
});
}
private void respawn(final CommandSource sender, final User teleportOwner, final User teleportee, final Trade charge) throws Exception {
private void respawn(final CommandSource sender, final User teleportOwner, final User teleportee, final Trade charge, String commandLabel, CompletableFuture<Boolean> future) throws Exception {
final SpawnStorage spawns = (SpawnStorage) this.module;
final Location spawn = spawns.getSpawn(teleportee.getGroup());
sender.sendMessage(tl("teleporting", spawn.getWorld().getName(), spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()));
future.exceptionally(e -> {
showError(sender.getSender(), e, commandLabel);
return false;
});
if (teleportOwner == null) {
teleportee.getTeleport().now(spawn, false, TeleportCause.COMMAND);
teleportee.getAsyncTeleport().now(spawn, false, TeleportCause.COMMAND, future);
} else {
teleportOwner.getTeleport().teleportPlayer(teleportee, spawn, charge, TeleportCause.COMMAND);
teleportOwner.getAsyncTeleport().teleportPlayer(teleportee, spawn, charge, TeleportCause.COMMAND, future);
}
}
}

View File

@ -17,6 +17,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -135,13 +136,14 @@ class EssentialsSpawnPlayerListener implements Listener {
return;
}
try {
final Location spawn = spawns.getSpawn(ess.getSettings().getNewbieSpawn());
if (spawn != null) {
user.getTeleport().now(spawn, false, TeleportCause.PLUGIN);
}
} catch (Exception ex) {
logger.log(Level.WARNING, tl("teleportNewPlayerError"), ex);
final Location spawn = spawns.getSpawn(ess.getSettings().getNewbieSpawn());
if (spawn != null) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(e -> {
logger.log(Level.WARNING, tl("teleportNewPlayerError"), e);
return false;
});
user.getAsyncTeleport().now(spawn, false, TeleportCause.PLUGIN, future);
}
}
}