TF-EssentialsX/Essentials/src/com/earth2me/essentials/Teleport.java

349 lines
9.7 KiB
Java

package com.earth2me.essentials;
import static com.earth2me.essentials.I18n._;
import com.earth2me.essentials.api.ITeleport;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.logging.Logger;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
public class Teleport implements Runnable, ITeleport
{
private static final double MOVE_CONSTANT = 0.3;
private class Target
{
private final Location location;
private final String name;
Target(Location location)
{
this.location = location;
this.name = null;
}
Target(Player entity)
{
this.name = entity.getName();
this.location = null;
}
public Location getLocation()
{
if (this.name != null)
{
return ess.getServer().getPlayerExact(name).getLocation();
}
return location;
}
}
private IUser user;
private IUser teleportUser;
private int teleTimer = -1;
private long started; // time this task was initiated
private long delay; // how long to delay the teleport
private int 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 long initX;
private long initY;
private long initZ;
private Target teleportTarget;
private Trade chargeFor;
private final IEssentials ess;
private static final Logger logger = Logger.getLogger("Minecraft");
private TeleportCause cause;
private void initTimer(long delay, Target target, Trade chargeFor, TeleportCause cause)
{
initTimer(delay, user, target, chargeFor, cause);
}
private void initTimer(long delay, IUser teleportUser, Target target, Trade chargeFor, TeleportCause cause)
{
this.started = System.currentTimeMillis();
this.delay = delay;
this.health = teleportUser.getHealth();
this.initX = Math.round(teleportUser.getLocation().getX() * MOVE_CONSTANT);
this.initY = Math.round(teleportUser.getLocation().getY() * MOVE_CONSTANT);
this.initZ = Math.round(teleportUser.getLocation().getZ() * MOVE_CONSTANT);
this.teleportUser = teleportUser;
this.teleportTarget = target;
this.chargeFor = chargeFor;
this.cause = cause;
}
@Override
public void run()
{
if (user == null || !user.isOnline() || user.getLocation() == null)
{
cancel(false);
return;
}
if (teleportUser == null || !teleportUser.isOnline() || teleportUser.getLocation() == null)
{
cancel(false);
return;
}
if (!user.isAuthorized("essentials.teleport.timer.move")
&& (Math.round(teleportUser.getLocation().getX() * MOVE_CONSTANT) != initX
|| Math.round(teleportUser.getLocation().getY() * MOVE_CONSTANT) != initY
|| Math.round(teleportUser.getLocation().getZ() * MOVE_CONSTANT) != initZ
|| teleportUser.getHealth() < health))
{
// user moved, cancel teleport
cancel(true);
return;
}
health = teleportUser.getHealth(); // in case user healed, then later gets injured
long now = System.currentTimeMillis();
if (now > started + delay)
{
try
{
cooldown(false);
teleportUser.sendMessage(_("teleportationCommencing"));
try
{
teleportUser.getTeleport().now(teleportTarget, cause);
cancel(false);
if (chargeFor != null)
{
chargeFor.charge(user);
}
}
catch (Throwable ex)
{
ess.showError(user.getBase(), ex, "teleport");
}
}
catch (Exception ex)
{
user.sendMessage(_("cooldownWithMessage", ex.getMessage()));
if (user != teleportUser)
{
teleportUser.sendMessage(_("cooldownWithMessage", ex.getMessage()));
}
}
}
}
public Teleport(IUser user, IEssentials ess)
{
this.user = user;
this.ess = ess;
}
public void cooldown(boolean check) throws Exception
{
final Calendar time = new GregorianCalendar();
if (user.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 teleport could have been used that would allow another use.
final long earliestLong = earliestTime.getTimeInMillis();
// When was the last teleport used?
final Long lastTime = user.getLastTeleportTimestamp();
if (lastTime > time.getTimeInMillis())
{
// This is to make sure time didn't get messed up on last kit use.
// If this happens, let's give the user the benifit of the doubt.
user.setLastTeleportTimestamp(time.getTimeInMillis());
return;
}
else if (lastTime > earliestLong && !user.isAuthorized("essentials.teleport.cooldown.bypass"))
{
time.setTimeInMillis(lastTime);
time.add(Calendar.SECOND, (int)cooldown);
time.add(Calendar.MILLISECOND, (int)((cooldown * 1000.0) % 1000.0));
throw new Exception(_("timeBeforeTeleport", Util.formatDateDiff(time.getTimeInMillis())));
}
}
// if justCheck is set, don't update lastTeleport; we're just checking
if (!check)
{
user.setLastTeleportTimestamp(time.getTimeInMillis());
}
}
//If we need to cancel a pending teleport call this method
public void cancel(boolean notifyUser)
{
if (teleTimer == -1)
{
return;
}
try
{
ess.getServer().getScheduler().cancelTask(teleTimer);
if (notifyUser)
{
user.sendMessage(_("pendingTeleportCancelled"));
if (teleportUser != user)
{
teleportUser.sendMessage(_("pendingTeleportCancelled"));
}
}
}
finally
{
teleTimer = -1;
}
}
//The now function is used when you want to skip tp delay when teleporting someone to a location or player.
public void now(Location loc, boolean cooldown, TeleportCause cause) throws Exception
{
if (cooldown)
{
cooldown(false);
}
now(new Target(loc), cause);
}
public void now(Player entity, boolean cooldown, TeleportCause cause) throws Exception
{
if (cooldown)
{
cooldown(false);
}
now(new Target(entity), cause);
}
private void now(Target target, TeleportCause cause) throws Exception
{
cancel(false);
user.setLastLocation();
user.getBase().teleport(Util.getSafeDestination(target.getLocation()), cause);
}
//The teleport function is used when you want to normally teleport someone to a location or player.
public void teleport(Location loc, Trade chargeFor, TeleportCause cause) throws Exception
{
teleport(new Target(loc), chargeFor, cause);
}
public void teleport(Player entity, Trade chargeFor, TeleportCause cause) throws Exception
{
teleport(new Target(entity), chargeFor, cause);
}
private void teleport(Target target, Trade chargeFor, TeleportCause cause) throws Exception
{
double delay = ess.getSettings().getTeleportDelay();
if (chargeFor != null)
{
chargeFor.isAffordableFor(user);
}
cooldown(true);
if (delay <= 0 || user.isAuthorized("essentials.teleport.timer.bypass"))
{
cooldown(false);
now(target, cause);
if (chargeFor != null)
{
chargeFor.charge(user);
}
return;
}
cancel(false);
warnUser(user);
initTimer((long)(delay * 1000.0), target, chargeFor, cause);
teleTimer = ess.scheduleSyncRepeatingTask(this, 10, 10);
}
//The teleportToMe function is a wrapper used to handle teleporting players to them, like /tphere
public void teleportToMe(User otherUser, Trade chargeFor, TeleportCause cause) throws Exception
{
Target target = new Target(user);
double delay = ess.getSettings().getTeleportDelay();
if (chargeFor != null)
{
chargeFor.isAffordableFor(user);
}
cooldown(true);
if (delay <= 0 || user.isAuthorized("essentials.teleport.timer.bypass"))
{
cooldown(false);
otherUser.getTeleport().now(target, cause);
if (chargeFor != null)
{
chargeFor.charge(user);
}
return;
}
cancel(false);
warnUser(otherUser);
initTimer((long)(delay * 1000.0), otherUser, target, chargeFor, cause);
teleTimer = ess.scheduleSyncRepeatingTask(this, 10, 10);
}
private void warnUser(final IUser user)
{
Calendar c = new GregorianCalendar();
c.add(Calendar.SECOND, (int)delay);
c.add(Calendar.MILLISECOND, (int)((delay * 1000.0) % 1000.0));
user.sendMessage(_("dontMoveMessage", Util.formatDateDiff(c.getTimeInMillis())));
}
//The respawn function is a wrapper used to handle tp fallback, on /jail and /home
public void respawn(final Trade chargeFor, TeleportCause cause) throws Exception
{
final Player player = user.getBase();
final Location bed = player.getBedSpawnLocation();
final PlayerRespawnEvent pre = new PlayerRespawnEvent(player, bed == null ? player.getWorld().getSpawnLocation() : bed, bed != null);
ess.getServer().getPluginManager().callEvent(pre);
teleport(new Target(pre.getRespawnLocation()), chargeFor, cause);
}
//The warp function is a wrapper used to teleport a player to a /warp
public void warp(String warp, Trade chargeFor, TeleportCause cause) throws Exception
{
Location loc = ess.getWarps().getWarp(warp);
teleport(new Target(loc), chargeFor, cause);
user.sendMessage(_("warpingTo", warp));
}
//The back function is a wrapper used to teleport a player /back to their previous location.
public void back(Trade chargeFor) throws Exception
{
teleport(new Target(user.getLastLocation()), chargeFor, TeleportCause.COMMAND);
}
//This function is used to throw a user back after a jail sentence
public void back() throws Exception
{
now(new Target(user.getLastLocation()), TeleportCause.COMMAND);
}
//This function handles teleporting to /home
public void home(Location loc, Trade chargeFor) throws Exception
{
teleport(new Target(loc), chargeFor, TeleportCause.COMMAND);
}
}