Package games.stendhal.server.core.scripting

Source Code of games.stendhal.server.core.scripting.ScriptRunner

/* $Id: ScriptRunner.java,v 1.19 2010/11/26 20:24:01 martinfuchs Exp $ */

package games.stendhal.server.core.scripting;

import games.stendhal.common.CommandlineParser;
import games.stendhal.common.ErrorBuffer;
import games.stendhal.common.ErrorDrain;
import games.stendhal.server.actions.ActionListener;
import games.stendhal.server.actions.CommandCenter;
import games.stendhal.server.actions.admin.AdministrationAction;
import games.stendhal.server.core.engine.GameEvent;
import games.stendhal.server.entity.player.Player;
import games.stendhal.server.extension.StendhalServerExtension;

import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import marauroa.common.game.RPAction;

import org.apache.log4j.Logger;

/**
* ServerExtension to load Groovy and Java scripts.
*
* @author intensifly
*/
public class ScriptRunner extends StendhalServerExtension implements
    ActionListener {

  private static final int REQUIRED_ADMINLEVEL = 1000;

  private final Map<String, ScriptingSandbox> scripts;

  private final String scriptDir = "data/script/";

  private static final Logger logger = Logger.getLogger(ScriptRunner.class);

  /**
   * Constructor for ScriptRunner.
   */
  public ScriptRunner() {
    super();
    scripts = new HashMap<String, ScriptingSandbox>();
    CommandCenter.register("script", this, REQUIRED_ADMINLEVEL);
  }

  @Override
  public void init() {
    final URL url = getClass().getClassLoader().getResource(scriptDir);
    if (url != null) {
      final File dir = new File(url.getFile());
      final String[] strs = dir.list(new FilenameFilter() {
        public boolean accept(final File dir, final String name) {
          return name.endsWith(".groovy");
        }
      });

      for (int i = 0; i < strs.length; i++) {
        try {
          perform(strs[i]);
        } catch (final Exception e) {
          logger.error("Error while loading " + strs[i] + ":", e);
        }
      }
    }

  }

  @Override
  public synchronized boolean perform(final String name) {
    return perform(name, "load", null, null);
  }
 
  // need that function to filter scripts names
  public String searchTermToRegex(String searchTerm) {
    final String metaSymbols = "*?()[]{}+-.^$|\\";
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < searchTerm.length(); i++) {
      final char c = searchTerm.charAt(i);
      final int n = metaSymbols.indexOf(c);
      if (n == -1) {
        stringBuilder.append(c);
      } else {
        switch (c) {
          case '*':
            stringBuilder.append(".*?");
            break;
          case '?':
            stringBuilder.append(".");
            break;
          default:
            stringBuilder.append('\\');
            stringBuilder.append(c);
            break;
        }
      }
    }
    return stringBuilder.toString();
  }
 
  private synchronized boolean perform(final String name, final String mode,
      final Player player, final List<String> args) {
    boolean ret = false;
   
    // block exploit
    if (name.indexOf("..") >= 0) {
      return false;
    }
   
    // list mode
    if ("list".equals(mode)) {
      return listScripts(player, args);
    }

    final String trimmedName = name.trim();
    // if the script is already running get it, else null it
    ScriptingSandbox script = scripts.get(trimmedName);

    // unloading
    if ("load".equals(mode) || "remove".equals(mode)
        || "unload".equals(mode)) {

      script = scripts.remove(trimmedName);
      if (script != null) {
        script.unload(player, args);
        ret = true;
      }
      script = null;
    }

    // load and/or execute
    if ("load".equals(mode) || "execute".equals(mode)) {
      boolean ignoreExecute = false;

      // load script if it is not already loaded,
      // if we want to execute we'll also load it if needed
      if (script == null) {
        if (trimmedName.endsWith(".groovy")) {
          script = new ScriptInGroovy(scriptDir + trimmedName);
          ignoreExecute = true;
        } else if (trimmedName.endsWith(".class")) {
          script = new ScriptInJava(trimmedName);
        }
        if (script != null) {
          ret = script.load(player, args);
          scripts.put(trimmedName, script);
        }
      }

      if ("execute".equals(mode) && !ignoreExecute) {
        if (script != null) {
          ret = script.execute(player, args);
        } else {
          logger.error("Script not executed: " + trimmedName);
        }
      }
    }

    return (ret);
  }

  /**
   * Lists the available scripts
   *
   * @param player player requesting the list
   * @param filterTerm filter
   * @return true
   */
  private boolean listScripts(final Player player, List<String> filterTerm) {
    // *.groovy scripts is in data/script/
    final File dirGroovy = new File(scriptDir);
    // *.class scripts is in data/script/games/stendhal/server/script/
    final File dirClasses = new File(scriptDir+"games/stendhal/server/script/");
    final String[] scriptsGroovy = dirGroovy.list(new FilenameFilter() {
        public boolean accept(final File dir, final String name) {
          return (name.endsWith(".groovy") && (name.indexOf('$') == -1));
        }
      });
    final String[] scriptsJava = dirClasses.list(new FilenameFilter(){
        public boolean accept(final File dir, final String name) {
          // remove filenames with '$' inside because they are inner classes
          return (name.endsWith(".class") && (name.indexOf('$') == -1));
        }
      });
           
    int scriptsGroovyLength = 0;
    if (scriptsGroovy!=null) {
      scriptsGroovyLength=scriptsGroovy.length;
    }
    int scriptsJavaLength = 0;
    if (scriptsJava!=null) {
      scriptsJavaLength=scriptsJava.length;
    }
    final int scriptsLength = scriptsGroovyLength + scriptsJavaLength;
    // concatenating String arrays scriptsGroovy and scriptsJava to scripts
    final String[] scripts= new String[scriptsLength];
   
    if (scriptsGroovy!=null) {
    System.arraycopy(scriptsGroovy, 0, scripts, 0, scriptsGroovyLength);
    }
   
    if (scriptsJava!=null) {
    System.arraycopy(scriptsJava, 0, scripts, scriptsGroovyLength, scriptsJavaLength);
    }
   
    StringBuilder stringBuilder = new StringBuilder();

    if (!filterTerm.isEmpty()) {
      stringBuilder.append("results for /script ");
      for (int i=0; i<filterTerm.size(); i++) {
        stringBuilder.append(" "+ filterTerm.get(i));
      }
      stringBuilder.append(":\n");
    }

    for (int i = 0; i < scriptsLength; i++) {
      // if arguments given, will look for matches.
      if (!filterTerm.isEmpty()) {
        int j = 0;         
        for (j = 0; j < filterTerm.size(); j++) {
          if (scripts[i].matches(searchTermToRegex(filterTerm.get(j)))) {
            stringBuilder.append(scripts[i]+"\n");
          }
        }
      } else {
        stringBuilder.append(scripts[i]+"\n");
      }
    } 
    stringBuilder.append("(end of listing).");
    player.sendPrivateText(stringBuilder.toString());
    return true;
  }

  @Override
  public String getMessage(final String name) {
    final ScriptingSandbox gr = scripts.get(name);
    if (gr != null) {
      return (gr.getMessage());
    }
    return (null);
  }

  public void onAction(final Player player, final RPAction action) {

    if (!AdministrationAction.isPlayerAllowedToExecuteAdminCommand(player, "script", true)) {
      return;
    }
    String text = "usage: #/script #[-list|-execute|-load|-unload] #<filename> #[<args>]\n mode is either load (default) or remove";
    if (action.has("target")) {

      // concat target and args to get the original string back
      String cmd = action.get("target");
      if (action.has("args")) {
        cmd = cmd + " " + action.get("args");
      }
      // in the simplest case there is only one argument which is the
      // script name
      String mode = "execute";
      String script = cmd;
     
      /*
       // parse args if there is a space
       int pos = cmd.indexOf(' ');
       if (pos > -1) {
        // Analyze the first word: mode or filename?
        String temp = cmd.substring(0, pos);
        cmd = cmd.substring(pos + 1);
        if (temp.startsWith("-")) {
          // it is "mode"
          mode = temp.substring(1);
          pos = cmd.indexOf(' ');

          if (pos > -1) {
            temp = cmd.substring(0, pos);
            cmd = cmd.substring(pos + 1);
          } else {
            temp = cmd;
          }
        }
        script = temp;
       }
       */
     
      // parts of script command.
      final List<String> parts = Arrays.asList(cmd.split(" "));
      cmd = "";
      int scp = 0;
      if (!parts.isEmpty()) {
        if (parts.get(0).startsWith("-")) {
          // it is "mode"
          mode = parts.get(0).substring(1);
          scp = 1;
        }
        // determine where is script name
        if (scp < parts.size()) {
          script = parts.get(scp);
        }
        StringBuilder sb = new StringBuilder(cmd);
        // concatenating script arguments
        for (int i = scp+1; i<parts.size(); i++) {
           sb.append(' ').append(parts.get(i));       
        }
        cmd = sb.toString();
        // for list mode we dont have script name
        if ("list".equals(mode)) {
           cmd = script + " " + cmd;
        }
      }

      // use the same routine as in the client to parse quoted arguments
      final CommandlineParser parser = new CommandlineParser(cmd);
      final ErrorDrain errors = new ErrorBuffer();

      final List<String> args = parser.readAllParameters(errors);

      new GameEvent(player.getName(), "script", script, mode, args.toString()).raise();

      // execute script
      script = script.trim();
      if ("list".equals(mode) || script.endsWith(".groovy") || script.endsWith(".class")) {
        boolean res = false;
        res = perform(script, mode, player, args);
        if (res) {
          StringBuilder stringBuilder = new StringBuilder();
          if (!"list".equals(mode)) {
          stringBuilder.append("Script \"");
          stringBuilder.append(script);
          stringBuilder.append("\" was successfully ");
          stringBuilder.append(mode);
          } else {
            // we dont want spam messages.
            //stringBuilder.append("script directories was successfully list");
            return;
          }
          if ("execute".equals(mode)) {
            stringBuilder.append("d");
          } else {
            stringBuilder.append("ed");
          }
          stringBuilder.append(".");
          text = stringBuilder.toString();
        } else {
          final String msg = getMessage(script);
          if (msg != null) {
            text = msg;
          } else {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("Script \"");
            stringBuilder.append(script);
            stringBuilder.append("\" was either not found, or encountered an error during ");
            if ("execute".equals(mode)) {
              stringBuilder.append("execution");
            } else {
              stringBuilder.append(mode);
              stringBuilder.append("ing");
            }
            stringBuilder.append(".");
            text = stringBuilder.toString();
          }
        }
      } else {
        text = "Invalid filename: It should end with .groovy or .class";
      }
    }

    player.sendPrivateText(text);
  }

}
TOP

Related Classes of games.stendhal.server.core.scripting.ScriptRunner

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.