Package org.spout.vanilla.component.block.material

Source Code of org.spout.vanilla.component.block.material.CommandBlock

/*
* This file is part of Vanilla.
*
* Copyright (c) 2011 Spout LLC <http://www.spout.org/>
* Vanilla is licensed under the Spout License Version 1.
*
* Vanilla is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* In addition, 180 days after any changes are published, you can use the
* software, incorporating those changes, under the terms of the MIT license,
* as described in the Spout License Version 1.
*
* Vanilla 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 Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License,
* the MIT license and the Spout License Version 1 along with this program.
* If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
* License and see <http://spout.in/licensev1> for the full license, including
* the MIT license.
*/
package org.spout.vanilla.component.block.material;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.builder.ToStringBuilder;

import org.spout.api.Server;
import org.spout.api.Spout;
import org.spout.api.command.Command;
import org.spout.api.command.CommandArguments;
import org.spout.api.command.CommandSource;
import org.spout.api.data.ValueHolder;
import org.spout.api.entity.Player;
import org.spout.api.event.Cause;
import org.spout.api.event.server.PreCommandEvent;
import org.spout.api.exception.CommandException;
import org.spout.api.geo.World;
import org.spout.api.geo.cuboid.Block;
import org.spout.api.geo.discrete.Point;
import org.spout.api.lang.Locale;
import org.spout.api.util.SpoutToStringStyle;

import org.spout.vanilla.ChatStyle;
import org.spout.vanilla.VanillaPlugin;
import org.spout.vanilla.component.block.VanillaBlockComponent;
import org.spout.vanilla.component.entity.living.Human;
import org.spout.vanilla.component.entity.misc.Level;
import org.spout.vanilla.data.GameMode;
import org.spout.vanilla.data.VanillaData;
import org.spout.vanilla.data.configuration.VanillaConfiguration;
import org.spout.vanilla.scoreboard.Objective;
import org.spout.vanilla.scoreboard.Scoreboard;
import org.spout.vanilla.scoreboard.Team;

public class CommandBlock extends VanillaBlockComponent implements CommandSource {
  public static final char TARGET_CHAR = '@';
  public static final char NEAREST_PLAYER_CHAR = 'p';
  public static final char RANDOM_PLAYER_CHAR = 'r';
  public static final char ALL_PLAYERS_CHAR = 'a';
  public static final String NEAREST_PLAYER = "" + TARGET_CHAR + NEAREST_PLAYER_CHAR;
  public static final String RANDOM_PLAYER = "" + TARGET_CHAR + RANDOM_PLAYER_CHAR;
  public static final String ALL_PLAYERS = "" + TARGET_CHAR + ALL_PLAYERS_CHAR;
  private String name = "" + TARGET_CHAR;

  /**
   * Sets the name displayed when a command is executed.
   *
   * @param name to display
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Sets the command executed when {@link #execute()} is called.
   *
   * @param cmd to execute
   * @param cause of command, can be null
   */
  public void setCommand(String cmd, Cause<?> cause) {
    if (cause != null && cause.getSource() instanceof CommandSource) {
      CommandSource cmdSource = (CommandSource) cause.getSource();
      String prefix = VanillaPlugin.getInstance().getPrefix();
      if (!cmdSource.hasPermission("vanilla.commandblock." + cmd.split(" ")[0])) {
        cmdSource.sendMessage(prefix + ChatStyle.RED + " You don't have permission to do that.");
        return;
      }
      cmdSource.sendMessage(prefix + ChatStyle.WHITE + "CommandDescription set: " + ChatStyle.GREEN + cmd);
    }

    if (!cmd.startsWith("/")) {
      cmd = "say " + cmd;
    } else {
      cmd = cmd.replaceFirst("/", "");
    }

    if (Spout.debugMode()) {
      Spout.getLogger().info("CommandBlock at " + getPoint().toString() + " command set to " + cmd);
    }

    getData().put(VanillaData.COMMAND, cmd);
  }

  /**
   * Sets the command to be executed on {@link #execute()}.
   *
   * @param cmd to execute
   */
  public void setCommand(String cmd) {
    setCommand(cmd, null);
  }

  /**
   * Returns the command to execute.
   *
   * @return command to execute
   */
  public String getCommand() {
    return getData().get(VanillaData.COMMAND);
  }

  /**
   * Sets whether the command block is powered.
   *
   * @param powered if this command block is powered
   */
  public void setPowered(boolean powered) {
    if (isPowered() != powered) {
      getData().put(VanillaData.IS_POWERED, powered);
      if (powered) {
        execute();
      }
    }
  }

  /**
   * Returns true if this command block is powered.
   *
   * @return true if powered
   */
  public boolean isPowered() {
    return getData().get(VanillaData.IS_POWERED);
  }

  /**
   * Executes the command specified by {@link #getCommand()}.
   */
  public void execute() {
    if (!(VanillaPlugin.getInstance().getEngine() instanceof Server)) {
      throw new IllegalStateException("CommandBlocks are run from the server.");
    }

    if (Spout.debugMode()) {
      Spout.getLogger().info("Executing CommandBlock at: " + getPoint().toString());
    }

    String cmd = getCommand();
    if (cmd == null) {
      return;
    }

    char[] chars = cmd.toCharArray();
    int targetIndex = -1;
    for (int i = 0; i < chars.length; i++) {
      if (chars[i] == TARGET_CHAR && i + 1 < chars.length && isTarget(chars[i + 1])) {
        // string contains an identifier
        targetIndex = i;
        break;
      }
    }

    List<String> cmds = new ArrayList<String>();
    if (targetIndex != -1) {

      // if we found a target, format the command
      final String statement = getStatement(cmd, targetIndex);
      final PlayerFilter filter = new PlayerFilter(statement);
      try {
        filter.init();
      } catch (IllegalArgumentException e) {
        Spout.getLogger().log(java.util.logging.Level.WARNING, "Could not execute CommandBlock at "
            + getBlock().getPosition().toString() + " because of illegal syntax.", e);
      }

      if (Spout.debugMode()) {
        Spout.getLogger().info("Statement: " + statement);
        Spout.getLogger().info("Corresponding Filter: " + filter);
      }

      final Point center = new Point(getBlock().getWorld(), filter.x, filter.y, filter.z);
      final char target = statement.charAt(1);
      final List<Player> allPlayers = Arrays.asList(((Server) Spout.getEngine()).getOnlinePlayers());

      switch (target) {

        case NEAREST_PLAYER_CHAR:
          // find the closest suitable player
          List<Player> nearbyPlayers = center.getWorld().getNearbyPlayers(center, filter.maxRadius);
          Player nearbyPlayer = filter.findPlayer(nearbyPlayers);
          if (nearbyPlayer == null) {
            return;
          }
          cmds.add(cmd.replace(statement, nearbyPlayer.getName()));
          break;

        case RANDOM_PLAYER_CHAR:
          // find a random but suitable player in any location
          Collections.shuffle(allPlayers);
          Player player = filter.findPlayer(allPlayers);
          if (player == null) {
            return;
          }
          cmds.add(cmd.replace(statement, player.getName()));
          break;

        case ALL_PLAYERS_CHAR:
          // use all players, but filter out any players that are not suitable
          for (Player p : filter.filter(allPlayers)) {
            cmds.add(cmd.replace(statement, p.getName()));
          }
          break;
      }
    } else {
      cmds.add(cmd);
    }

    for (String c : cmds) {
      // send commands
      String root = c.split(" ")[0];
      int argsIndex = c.indexOf(' ') + 1;
      String[] args = argsIndex > 0 ? c.substring(argsIndex).split(" ") : new String[0];
      processCommand(root, args);
    }
  }

  private static boolean isTarget(char c) {
    return c == NEAREST_PLAYER_CHAR || c == RANDOM_PLAYER_CHAR || c == ALL_PLAYERS_CHAR;
  }

  private static String getStatement(String cmd, int index) {
    cmd = cmd.substring(index);
    if (cmd.length() > 6 && cmd.charAt(2) == '[' && cmd.contains("]")) {
      return cmd.substring(0, cmd.indexOf("]") + 1);
    }
    return cmd.substring(0, 2);
  }

  private class PlayerFilter {
    private final Block block = getBlock();
    private final String statement;
    private int x = block.getX();
    private int y = block.getY();
    private int z = block.getZ();
    private int maxRadius = -1;
    private int minRadius = -1;
    private GameMode mode = null;
    private int playerListSize = 0;
    private boolean reversedList = false;
    private int maxExpLevel = -1;
    private int minExpLevel = -1;
    private String playerName = null;
    private boolean playerNameInverted = false;
    private String teamName = null;
    private boolean teamNameInverted = false;
    private final Map<String, Integer> minScores = new HashMap<String, Integer>();
    private final Map<String, Integer> maxScores = new HashMap<String, Integer>();

    public PlayerFilter(String statement) {
      this.statement = statement;
    }

    public void init() throws IllegalArgumentException {
      /*
       * Accepted syntax:
       * @<target>[<argument>=<v>,...]
       *
       * Arguments:
       * x: x-coordinate of search center
       * y: y-coordinate of search center
       * z: z-coordinate of search center
       * r: maximum radius of search
       * rm: minimum radius of search
       * m: player's gamemode
       * c: number of players to include (reversed list if negative)
       * l: maximum experience of players
       * ml: minimum experience of players
       * <objective>: maximum score of 'objective'
       * <objective>_min: minimum score of 'objective'
       */

      if (!statement.startsWith("@")) {
        throw new IllegalArgumentException("Arguments must begin with an '@' symbol.");
      }

      int startArgs = statement.indexOf("["), endArgs = statement.indexOf("]");
      if (startArgs != -1 && endArgs != -1) {
        // has arguments
        String[] args = statement.substring(startArgs + 1, endArgs).split(",");
        // check first 4 args for the shortcut syntax
        for (int i = 0; i < 4; i++) {
          String arg = args[i];
          if (arg == null || arg.contains("=")) {
            // null arg or arg has a v break
            continue;
          }
          switch (i) {
            case 0:
              x = parseInt(arg);
              break;
            case 1:
              y = parseInt(arg);
              break;
            case 2:
              z = parseInt(arg);
              break;
            case 3:
              maxRadius = parseInt(arg);
              break;
          }
        }

        for (String arg : args) {
          if (arg == null || !arg.contains("=")) {
            continue;
          }
          String[] s = arg.split("=");
          String a = s[0];
          String v = s[1];
          if (a.equalsIgnoreCase("x")) {
            x = parseInt(v);
          } else if (a.equalsIgnoreCase("y")) {
            y = parseInt(v);
          } else if (a.equalsIgnoreCase("z")) {
            z = parseInt(v);
          } else if (a.equalsIgnoreCase("r")) {
            maxRadius = parseInt(v);
          } else if (a.equalsIgnoreCase("rm")) {
            minRadius = parseInt(v);
          } else if (a.equalsIgnoreCase("m")) {
            mode = GameMode.get(parseInt(v));
          } else if (a.equalsIgnoreCase("c")) {
            int vv = parseInt(v);
            if (vv < 0) {
              reversedList = true;
            }
            playerListSize = Math.abs(vv);
          } else if (a.equalsIgnoreCase("l")) {
            maxExpLevel = parseInt(v);
          } else if (a.equalsIgnoreCase("lm")) {
            minExpLevel = parseInt(v);
          } else if (a.equals("name")) {
            playerNameInverted = v.startsWith("!");
            playerName = playerNameInverted ? v.substring(1) : v;
          } else if (a.equals("team")) {
            teamNameInverted = v.startsWith("!");
            teamName = teamNameInverted ? v.substring(1) : v;
          } else if (a.startsWith("score_")) {
            int value = parseInt(v);
            String obj = a.replace("score_", "");
            if (a.endsWith("_min")) {
              minScores.put(obj.replace("_min", ""), value);
            } else {
              maxScores.put(obj, value);
            }
          }
        }
      }
    }

    private int parseInt(String str) {
      try {
        return Integer.parseInt(str);
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException("Specified argument '" + str + "' was a String, int expected.", e);
      }
    }

    public Player findPlayer(List<Player> players) {
      for (Player p : players) {
        if (accept(p)) {
          return p;
        }
      }
      return null;
    }

    public List<Player> filter(List<Player> players) {
      if (reversedList) {
        Collections.reverse(players);
      }
      List<Player> filteredPlayers = new ArrayList<Player>();
      int entries = 0;
      for (Player player : players) {
        if (accept(player) && (playerListSize == 0 || entries < playerListSize)) {
          entries++;
          filteredPlayers.add(player);
        }
      }
      return filteredPlayers;
    }

    public boolean accept(Player p) {
      Point center = new Point(block.getWorld(), x, y, z);
      int distance = Math.round((float) p.getPhysics().getPosition().distance(center)); // the distance from the cmd block

      // get various principles (these all default to true if not specified)
      boolean closeEnough = (minRadius == -1 || distance >= minRadius) && (maxRadius == -1 || distance <= maxRadius);
      boolean correctMode = mode == null || mode.equals(p.get(Human.class).getGameMode());
      int lvl = p.get(Level.class).getLevel();
      boolean enoughExp = (minExpLevel == -1 || lvl >= minExpLevel) && (maxExpLevel == -1 || lvl <= maxExpLevel);

      // check if the name is correct if specified
      boolean correctName = playerName == null; // default to whether the player name is set
      if (playerName != null) {
        if (playerNameInverted) {
          // make sure the player does not have the specified name
          correctName = !p.getName().equalsIgnoreCase(playerName);
        } else {
          // make sure the player does have the specified name
          correctName = p.getName().equals(playerName);
        }
      }

      // check if the team is correct if specified
      Scoreboard board = p.get(Scoreboard.class);
      boolean correctTeam = teamName == null; // default to whether the team name is set
      if (teamName != null && board != null) {
        Team team = board.getTeam(teamName);
        if (team != null) {
          if (teamNameInverted) {
            // make sure the player is not on the specified team
            correctTeam = !team.getPlayerNames().contains(p.getName());
          } else {
            // make sure the player is on the specified team
            correctTeam = team.getPlayerNames().contains(p.getName());
          }
        }
      }

      // make sure the player has the right amount of points for the specified objectives
      boolean enoughPoints = minScores.isEmpty() && maxScores.isEmpty(); // default to whether there are any objectives set
      if (board != null) {
        // first make sure the player has the minimum requirements
        for (Map.Entry<String, Integer> e : minScores.entrySet()) {
          Objective obj = board.getObjective(e.getKey());
          if (obj == null) {
            continue;
          }

          int score = obj.getScore(p.getName()); // player's score for specified objective
          int minScore = e.getValue(); // specified minimum score
          enoughPoints = score >= minScore;
          if (!enoughPoints) {
            // not enough? break.
            break;
          }
        }

        // next make sure the player isn't over the maximums
        for (Map.Entry<String, Integer> e : maxScores.entrySet()) {
          Objective obj = board.getObjective(e.getKey());
          if (obj == null) {
            continue;
          }

          int score = obj.getScore(p.getName()); // player's score for specified objective
          int maxScore = e.getValue(); // specified maximum score
          enoughPoints = score <= maxScore;
          if (!enoughPoints) {
            // too many points? break.
            break;
          }
        }
      }

      if (Spout.debugMode()) {
        Spout.log("Trying to accept: " + p.getName());
        Spout.log("\tDistance From Origin: " + distance);
        Spout.log("\tClose Enough: " + closeEnough);
        Spout.log("\tCorrect Mode: " + correctMode);
        Spout.log("\tEnough Exp: " + enoughExp);
        Spout.log("\tCorrect Name: " + correctName);
        Spout.log("\tCorrect Team: " + correctTeam);
        Spout.log("\tEnough Points: " + enoughPoints);
      }

      return closeEnough && correctMode && enoughExp && correctName && correctTeam && enoughPoints;
    }

    @Override
    public String toString() {
      return new ToStringBuilder(SpoutToStringStyle.INSTANCE)
          .append("x", x)
          .append("y", y)
          .append("z", z)
          .append("max_radius", maxRadius)
          .append("min_radius", minRadius)
          .append("mode", mode)
          .append("list_size", playerListSize)
          .append("reversed_list", reversedList)
          .append("max_exp_lvl", maxExpLevel)
          .append("min_exp_lvl", minExpLevel)
          .append("player_name", playerName)
          .append("player_name_inverted", playerNameInverted)
          .append("team_name", teamName)
          .append("team_name_inverted", teamNameInverted)
          .append("min_scores", minScores)
          .append("max_scores", maxScores)
          .toString();
    }
  }

  @Override
  public void sendMessage(String message) {
    if (VanillaConfiguration.COMMAND_BLOCK_VERBOSE.getBoolean()) {
      Spout.getLogger().info("[ " + getName() + "] " + message);
    }
  }

  @Override
  public void processCommand(String command, String... args) {
    PreCommandEvent event = VanillaPlugin.getInstance().getEngine().getEventManager().callEvent(new PreCommandEvent(this, command, args));
    if (event.isCancelled()) {
      return;
    }
    command = event.getCommand();
    CommandArguments arguments = event.getArguments();

    if (Spout.debugMode()) {
      Spout.getLogger().info("Executing: " + command + " " + arguments);
    }

    Command cmd = Spout.getCommandManager().getCommand(command, false);
    if (cmd == null) {
      Spout.getLogger().warning("CommandBlock tried to send unknown command: " + command);
      return;
    }

    try {
      cmd.process(this, args);
    } catch (CommandException e) {
      sendMessage(ChatStyle.RED + e.getMessage());
    }
  }

  @Override
  public Locale getPreferredLocale() {
    return Locale.ENGLISH_US;
  }

  @Override
  public boolean hasPermission(String node) {
    return true;
  }

  @Override
  public boolean hasPermission(World world, String node) {
    return true;
  }

  @Override
  public boolean isInGroup(String group) {
    return false;
  }

  @Override
  public boolean isInGroup(World world, String group) {
    return false;
  }

  @Override
  public String[] getGroups() {
    return new String[0];
  }

  @Override
  public String[] getGroups(World world) {
    return new String[0];
  }

  @Override
  public ValueHolder getData(String node) {
    return null;
  }

  @Override
  public ValueHolder getData(World world, String node) {
    return null;
  }

  @Override
  public boolean hasData(String node) {
    return false;
  }

  @Override
  public boolean hasData(World world, String node) {
    return false;
  }

  @Override
  public String getName() {
    return name;
  }
}
TOP

Related Classes of org.spout.vanilla.component.block.material.CommandBlock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.