@Command(
      aliases = {"/redo", "redo"},
      usage = "[times] [player]",
      desc = "Redoes the last action (from history)",
      min = 0,
      max = 2)
  @CommandPermissions("worldedit.history.redo")
  public void redo(
      CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession)
      throws WorldEditException {

    int times = Math.max(1, args.getInteger(0, 1));

    for (int i = 0; i < times; ++i) {
      EditSession redone;
      if (args.argsLength() < 2) {
        redone = session.redo(session.getBlockBag(player));
      } else {
        player.checkPermission("worldedit.history.redo.other");
        LocalSession sess = we.getSession(args.getString(1));
        if (sess == null) {
          player.printError("Unable to find session for " + args.getString(1));
          break;
        }
        redone = sess.redo(session.getBlockBag(player));
      }
      if (redone != null) {
        player.print("Redo successful.");
        we.flushBlockBag(player, redone);
      } else {
        player.printError("Nothing left to redo.");
      }
    }
  }
  /**
   * Gets the region selection for the player.
   *
   * @param player
   * @return the selection or null if there was none
   */
  public Selection getSelection(Player player) {
    if (player == null) {
      throw new IllegalArgumentException("Null player not allowed");
    }
    if (!player.isOnline()) {
      throw new IllegalArgumentException("Offline player not allowed");
    }

    LocalSession session = controller.getSession(wrapPlayer(player));
    RegionSelector selector =
        session.getRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()));

    try {
      Region region = selector.getRegion();
      World world = ((BukkitWorld) session.getSelectionWorld()).getWorld();

      if (region instanceof CuboidRegion) {
        return new CuboidSelection(world, selector, (CuboidRegion) region);
      } else if (region instanceof Polygonal2DRegion) {
        return new Polygonal2DSelection(world, selector, (Polygonal2DRegion) region);
      } else {
        return null;
      }
    } catch (IncompleteRegionException e) {
      return null;
    }
  }
  /**
   * Remember an edit session.
   *
   * @param player
   * @param editSession
   */
  public void remember(Player player, EditSession editSession) {
    LocalPlayer wePlayer = wrapPlayer(player);
    LocalSession session = controller.getSession(wePlayer);

    session.remember(editSession);
    editSession.flushQueue();

    controller.flushBlockBag(wePlayer, editSession);
  }
  /**
   * Wrap an operation into an EditSession.
   *
   * @param player
   * @param op
   * @throws Throwable
   */
  public void perform(Player player, WorldEditOperation op) throws Throwable {
    LocalPlayer wePlayer = wrapPlayer(player);
    LocalSession session = controller.getSession(wePlayer);

    EditSession editSession = createEditSession(player);
    try {
      op.run(session, wePlayer, editSession);
    } finally {
      remember(player, editSession);
    }
  }
  /**
   * Gets the session for the player.
   *
   * @param player
   * @return
   */
  public EditSession createEditSession(Player player) {
    LocalPlayer wePlayer = wrapPlayer(player);
    LocalSession session = controller.getSession(wePlayer);
    BlockBag blockBag = session.getBlockBag(wePlayer);

    EditSession editSession =
        controller
            .getEditSessionFactory()
            .getEditSession(wePlayer.getWorld(), session.getBlockChangeLimit(), blockBag, wePlayer);
    editSession.enableQueue();

    return editSession;
  }
  /**
   * Sets the region selection for a player.
   *
   * @param player
   * @param selection
   */
  public void setSelection(Player player, Selection selection) {
    if (player == null) {
      throw new IllegalArgumentException("Null player not allowed");
    }
    if (!player.isOnline()) {
      throw new IllegalArgumentException("Offline player not allowed");
    }
    if (selection == null) {
      throw new IllegalArgumentException("Null selection not allowed");
    }

    LocalSession session = controller.getSession(wrapPlayer(player));
    RegionSelector sel = selection.getRegionSelector();
    session.setRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()), sel);
    session.dispatchCUISelection(wrapPlayer(player));
  }
 /** Called on plugin disable. */
 @Override
 public void onDisable() {
   for (Player player : getServer().getOnlinePlayers()) {
     LocalPlayer lPlayer = wrapPlayer(player);
     if (controller.getSession(lPlayer).hasCUISupport()) {
       lPlayer.dispatchCUIHandshake();
     }
   }
   controller.clearSessions();
   for (Handler h : controller.commandLogger.getHandlers()) {
     h.close();
   }
   config.unload();
   server.unregisterCommands();
   this.getServer().getScheduler().cancelTasks(this);
 }
 /**
  * Gets the session for the player.
  *
  * @param player
  * @return
  */
 public LocalSession getSession(Player player) {
   return controller.getSession(wrapPlayer(player));
 }