/* * CommandBook * Copyright (C) 2011 sk89q <http://www.sk89q.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.sk89q.commandbook.locations; import com.google.common.collect.Lists; import com.sk89q.commandbook.CommandBook; import com.sk89q.commandbook.session.SessionComponent; import com.sk89q.commandbook.session.SessionFactory; import com.sk89q.commandbook.util.ChatUtil; import com.sk89q.commandbook.util.InputUtil; import com.sk89q.commandbook.util.LocationUtil; import com.sk89q.commandbook.util.entity.player.PlayerUtil; import com.sk89q.commandbook.util.entity.player.iterators.TeleportPlayerIterator; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.zachsthings.libcomponents.ComponentInformation; import com.zachsthings.libcomponents.Depend; import com.zachsthings.libcomponents.InjectComponent; import com.zachsthings.libcomponents.bukkit.BasePlugin; import com.zachsthings.libcomponents.bukkit.BukkitComponent; import com.zachsthings.libcomponents.config.ConfigurationBase; import com.zachsthings.libcomponents.config.Setting; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; @ComponentInformation(friendlyName = "Teleports", desc = "Teleport-related commands") @Depend(components = SessionComponent.class) public class TeleportComponent extends BukkitComponent implements Listener { @InjectComponent private SessionComponent sessions; private LocalConfiguration config; @Override public void enable() { CommandBook.registerEvents(this); registerCommands(Commands.class); config = configure(new LocalConfiguration()); sessions.registerSessionFactory(TeleportSession.class, new SessionFactory<TeleportSession>() { @Override public TeleportSession createSession(CommandSender user) { return new TeleportSession(TeleportComponent.this); } }); } @Override public void reload() { configure(config); } public LocalConfiguration getConfig() { return config; } public static class LocalConfiguration extends ConfigurationBase { @Setting("call-message.sender") public String callMessageSender = "`yTeleport request sent."; @Setting("call-message.target") public String callMessageTarget = "`c**TELEPORT** %cname%`c requests a teleport! Use /bring <name> to accept."; @Setting("call-message.too-soon") public String callMessageTooSoon = "Wait a bit before asking again."; @Setting("bring-message.sender") public String bringMessageSender = "`yPlayer teleported."; @Setting("bring-message.target") public String bringMessageTarget = "`yYour teleport request to %cname%`y was accepted."; @Setting("bring-message.no-perm") public String bringMessageNoPerm = "That person didn't request a " + "teleport (recently) and you don't have permission to teleport anyone."; } // -- Event handlers @EventHandler public void onRespawn(PlayerRespawnEvent event) { sessions.getSession(TeleportSession.class, event.getPlayer()).rememberLocation(event.getPlayer()); } @EventHandler public void onTeleport(PlayerTeleportEvent event) { Location loc = event.getTo(); Player player = event.getPlayer(); if (event.isCancelled()) { return; } Location ignored = sessions.getSession(TeleportSession.class, player).getIgnoreLocation(); if (ignored != null) { if (ignored.getWorld().equals(loc.getWorld()) && ignored.distanceSquared(loc) <= 2) { return; } else { sessions.getSession(TeleportSession.class, player).setIgnoreLocation(null); } } sessions.getSession(TeleportSession.class, player).rememberLocation(event.getPlayer()); } public class Commands { @Command(aliases = {"teleport", "tp"}, usage = "[target] <destination>", desc = "Teleport to a location", flags = "s", min = 1, max = 4) @CommandPermissions({"commandbook.teleport"}) public void teleport(CommandContext args, CommandSender sender) throws CommandException { Iterable<Player> targets; final Location loc; boolean[] relative = new boolean[]{false, false, false}; /* 1. /tp playerTarget x y z (4 args) - VANILLA 2. /tp x y z (3 args) 3. /tp playerTarget x z (3 args) - NOT SUPPORTED 4. /tp playerTarget x,y,z (2 args) 5. /tp playerTarget playerDest (2 args) - VANILLA 6. /tp x z (2 args) - NOT SUPPORTED 7. /tp x,y,z (1 arg) 8. /tp playerDest (1 arg) - VANILLA */ // TODO: reduce code duplication, currently just trying to catch every case if (args.argsLength() == 1) { loc = InputUtil.LocationParser.matchLocation(sender, args.getString(0)); // matches both #7 and #8 // go to the center of the block if we're on the edge if (loc.getX() == loc.getBlockX()) loc.add(0.5, 0, 0); if (loc.getZ() == loc.getBlockZ()) loc.add(0, 0, 0.5); targets = Lists.newArrayList(PlayerUtil.checkPlayer(sender)); } else if (args.argsLength() == 2) { targets = InputUtil.PlayerParser.matchPlayers(sender, args.getString(0)); loc = InputUtil.LocationParser.matchLocation(sender, args.getString(1)); // matches both #4 and #5 if (loc.getX() == loc.getBlockX()) loc.add(0.5, 0, 0); if (loc.getZ() == loc.getBlockZ()) loc.add(0, 0, 0.5); } else if (args.argsLength() == 3) { // matches #2 - can only be used by a player targets = Lists.newArrayList(PlayerUtil.checkPlayer(sender)); double x = args.getDouble(0); double y = args.getDouble(1); double z = args.getDouble(2); loc = new Location((PlayerUtil.checkPlayer(sender)).getWorld(), x, y, z); if (loc.getX() == loc.getBlockX()) loc.add(0.5, 0, 0); if (loc.getZ() == loc.getBlockZ()) loc.add(0, 0, 0.5); // check location permission CommandBook.inst().checkPermission(sender, loc.getWorld(), "commandbook.locations.coords"); } else if (args.argsLength() == 4) { targets = InputUtil.PlayerParser.matchPlayers(sender, args.getString(0)); // matches #1 // support relative location (~5 -> current coord + 5) String xArg = args.getString(1); String yArg = args.getString(2); String zArg = args.getString(3); CommandBook.inst().checkPermission(sender, "commandbook.locations.coords"); if (xArg.startsWith("~")) relative[0] = true; if (yArg.startsWith("~")) relative[1] = true; if (zArg.startsWith("~")) relative[2] = true; if (relative[0] || relative[1] || relative[2]) { CommandBook.inst().checkPermission(sender, "commandbook.locations.coords.relative"); } double x = Double.parseDouble(xArg.replace("~", "")); double y = Double.parseDouble(yArg.replace("~", "")); double z = Double.parseDouble(zArg.replace("~", "")); World world; try { // for CommandBlock support world = LocationUtil.extractWorld(sender); } catch (Throwable t) { if (sender instanceof Player) { world = ((Player) sender).getWorld(); } else { world = BasePlugin.server().getWorlds().get(0); } } loc = new Location(world, x, y, z); if (loc.getX() == loc.getBlockX()) loc.add(0.5, 0, 0); if (loc.getZ() == loc.getBlockZ()) loc.add(0, 0, 0.5); } else { // this can't actually happen unless someone constructs their own CommandContext throw new CommandException("Invalid number of args."); } boolean hasTeleOtherCurrent = CommandBook.inst().hasPermission(sender, "commandbook.teleport.other"); boolean hasTeleOtherTo = CommandBook.inst().hasPermission(sender, loc.getWorld(), "commandbook.teleport.other"); for (Player target : targets) { if (target != sender) { // If any of the targets is not the sender, we need to check .other // we must check for the from (target's world), current (sender's world), // and to (target location's world). if (!loc.getWorld().equals(target.getWorld())) { CommandBook.inst().checkPermission(sender, target.getWorld(), "commandbook.teleport.other"); } // If either check has failed, check both to get the proper exception response if (!hasTeleOtherCurrent || !hasTeleOtherTo) { CommandBook.inst().checkPermission(sender, "commandbook.teleport.other"); CommandBook.inst().checkPermission(sender, loc.getWorld(), "commandbook.teleport.other"); } } else { // If the target is the sender, just check the to (target location's world) // the current has already been checked CommandBook.inst().checkPermission(sender, loc.getWorld(), "commandbook.teleport"); } } (new TeleportPlayerIterator(sender, loc, args.hasFlag('s'), relative)).iterate(targets); } @Command(aliases = {"call", "tpa"}, usage = "<target>", desc = "Request a teleport", min = 1, max = 1) @CommandPermissions({"commandbook.call"}) public void requestTeleport(CommandContext args, CommandSender sender) throws CommandException { Player player = PlayerUtil.checkPlayer(sender); Player target = InputUtil.PlayerParser.matchSinglePlayer(sender, args.getString(0)); CommandBook.inst().checkPermission(sender, target.getWorld(), "commandbook.call"); sessions.getSession(TeleportSession.class, player).checkLastTeleportRequest(target); sessions.getSession(TeleportSession.class, target).addBringable(player); String senderMessage = ChatUtil.replaceColorMacros( ChatUtil.replaceMacros(sender, config.callMessageSender)) .replaceAll("%ctarget%", ChatUtil.toColoredName(target, null)) .replaceAll("%target%", ChatUtil.toName(target)); String targetMessage = ChatUtil.replaceColorMacros( ChatUtil.replaceMacros(sender, config.callMessageTarget)) .replaceAll("%ctarget%", ChatUtil.toColoredName(target, null)) .replaceAll("%target%", ChatUtil.toName(target)); sender.sendMessage(senderMessage); target.sendMessage(targetMessage); } @Command(aliases = {"bring", "tphere", "grab", "g"}, usage = "<target>", desc = "Bring a player to you", min = 1, max = 1) public void bring(CommandContext args, CommandSender sender) throws CommandException { Player player = PlayerUtil.checkPlayer(sender); Player target = null; // If we have a single player match, check that for bringable. If we do not, // then check to see if the player can bring multiple players in his current // world. If that is the case then allow this to continue. try { target = InputUtil.PlayerParser.matchSinglePlayer(sender, args.getString(0)); } catch (CommandException ex) { if (!CommandBook.inst().hasPermission(sender, "commandbook.teleport.other")) { // There was not a single player match, and the player does not have // permission to teleport players in his/her current world. throw ex; } } if (target != null) { if (sessions.getSession(TeleportSession.class, player).isBringable(target)) { final String senderMessage = ChatUtil.replaceColorMacros( ChatUtil.replaceMacros(sender, config.bringMessageSender)) .replaceAll("%ctarget%", ChatUtil.toColoredName(target, null)) .replaceAll("%target%", target.getName()); final String targetMessage = ChatUtil.replaceColorMacros( ChatUtil.replaceMacros(sender, config.bringMessageTarget)) .replaceAll("%ctarget%", ChatUtil.toColoredName(target, null)) .replaceAll("%target%", target.getName()); (new TeleportPlayerIterator(sender, player.getLocation()) { @Override public void onVictim(CommandSender sender, Player player) { player.sendMessage(targetMessage); } @Override public void onInformMany(CommandSender sender, int affected) { sender.sendMessage(senderMessage); } }).iterate(Lists.newArrayList(target)); return; } else if (!CommandBook.inst().hasPermission(sender, "commandbook.teleport.other")) { // There was a single player match, but, the target was not bringable, and the player // does not have permission to teleport players in his/her current world. throw new CommandException(config.bringMessageNoPerm); } } Location loc = player.getLocation(); // There was not a single player match, or that single player match did not request // to be brought. The player does have permission to teleport players in his/her // current world. However, we're now teleporting in targets from potentially different worlds, // and we should ensure that the sender has permission to teleport players in those worlds. Iterable<Player> targets = InputUtil.PlayerParser.matchPlayers(sender, args.getString(0)); for (Player aTarget : targets) { // We have already checked the from and current locations, we must now check the to if the world does not match if (!loc.getWorld().equals(aTarget.getWorld())) { CommandBook.inst().checkPermission(sender, aTarget.getWorld(), "commandbook.teleport.other"); } } (new TeleportPlayerIterator(sender, loc)).iterate(targets); } @Command(aliases = {"put", "place"}, usage = "<target>", desc = "Put a player at where you are looking", min = 1, max = 1) @CommandPermissions({"commandbook.teleport.other"}) public void put(CommandContext args, CommandSender sender) throws CommandException { Iterable<Player> targets = InputUtil.PlayerParser.matchPlayers(sender, args.getString(0)); Location loc = InputUtil.LocationParser.matchLocation(sender, "#target"); for (Player target : targets) { // We have already checked the from and current locations, we must now check the to if the world does not match if (!loc.getWorld().equals(target.getWorld())) { CommandBook.inst().checkPermission(sender, target.getWorld(), "commandbook.teleport.other"); } } (new TeleportPlayerIterator(sender, loc)).iterate(targets); } @Command(aliases = {"return", "ret"}, usage = "[player]", desc = "Teleport back to your last location", min = 0, max = 1) @CommandPermissions({"commandbook.return"}) public void ret(CommandContext args, CommandSender sender) throws CommandException { Player player; if (args.argsLength() > 0) { player = InputUtil.PlayerParser.matchSinglePlayer(sender, args.getString(0)); if (player != sender) { CommandBook.inst().checkPermission(sender, "commandbook.return.other"); } } else { player = PlayerUtil.checkPlayer(sender); } Location lastLoc = sessions.getSession(TeleportSession.class, player).popLastLocation(); if (lastLoc != null) { sessions.getSession(TeleportSession.class, player).setIgnoreLocation(lastLoc); lastLoc.getChunk().load(true); (new TeleportPlayerIterator(sender, lastLoc) { @Override public void onCaller(Player player) { sender.sendMessage(ChatColor.YELLOW + "You've been returned."); } @Override public void onVictim(CommandSender sender, Player player) { player.sendMessage(ChatColor.YELLOW + "You've been returned by " + ChatUtil.toColoredName(sender, ChatColor.YELLOW) + "."); } @Override public void onInformMany(CommandSender sender, int affected) { sender.sendMessage(ChatColor.YELLOW.toString() + affected + " returned."); } }).iterate(Lists.newArrayList(player)); } else { sender.sendMessage(ChatColor.RED + "There's no past location in your history."); } } } }