Package simpleserver.events

Source Code of simpleserver.events.RunningEvent

/*
* Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package simpleserver.events;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import simpleserver.Coordinate;
import simpleserver.Player;
import simpleserver.Server;
import simpleserver.bot.BotController.ConnectException;
import simpleserver.bot.NpcBot;
import simpleserver.command.ExternalCommand;
import simpleserver.command.PlayerCommand;
import simpleserver.command.ServerCommand;
import simpleserver.config.xml.CommandConfig;
import simpleserver.config.xml.CommandConfig.Forwarding;
import simpleserver.config.xml.Event;
import simpleserver.log.EventsLog;


class RunningEvent extends Thread implements Runnable {

  /**
   *
   */
  public boolean stopping = false; // indicates that the thread should stop

  protected EventHost eventHost;
  protected Server server;
  protected Event event; // currently run event
  protected Player player; // referred player context
  // (default -> triggering player)
  private String threadname = null; // null = just subroutine

  protected static final int MAXDEPTH = 5;

  protected static final char LOCALSCOPE = '#';
  protected static final char PLAYERSCOPE = '@';
  protected static final char GLOBALSCOPE = '$';
  protected static final char REFERENCEOP = '\'';

  private HashMap<String, String> vars; // local vars
  private ArrayList<String> threadstack; // thread-shared data stack

  // for control structures
  private int stackdepth = 0; // to limit subroutines
  private String lastline; // line being executed
  private int currline = 0; // execution line pointer

  private ArrayList<Integer> ifstack;
  private ArrayList<Integer> whilestack;

  private boolean running = false;

  public RunningEvent(EventHost h, String n, Event e, Player p) {
    eventHost = h;
    server = h.server;
    event = e;
    player = p;
    threadname = n;

    // Top level event without initialized stack -> initialize empty data stack
    threadstack = new ArrayList<String>();

    vars = new HashMap<String, String>();
    ifstack = new ArrayList<Integer>();
    whilestack = new ArrayList<Integer>();
  }

  /* init as subroutine */
  public RunningEvent(EventHost h, String n, Event e, Player p, int depth, ArrayList<String> stack) {
    this(h, n, e, p);
    stackdepth = depth;
    if (stack != null) {
      threadstack = stack;
    }
  }

  @Override
  public void run() {
    if (running) { // prevent running the same object twice
      return;
    }
    running = true;

    String script = event.script;
    if (script == null)
    {
      return; // no script data present
    }

    // Split actions by semicola and newlines
    ArrayList<String> actions =
        new ArrayList<String>(java.util.Arrays.asList(script.split("[\n;]")));
    currline = 0;

    while (currline < actions.size()) {
      if (stopping) {
        break;
      }

      lastline = actions.get(currline);

      ArrayList<String> tokens = parseLine(lastline);
      evaluateVariables(tokens);

      if (tokens.size() == 0) {
        currline++;
        continue;
      }

      // run action
      String cmd = tokens.remove(0);
      if (cmd.equals("return")) {
        cmdreturn(tokens);
        return;
      } else if (cmd.equals("rem") || cmd.equals("endif")) {
        currline++;
        continue;
      } else if (cmd.equals("print") && tokens.size() > 0) {
        System.out.println("L" + String.valueOf(currline) + "@" + event.name + " msg: " + tokens.get(0));
      } else if (cmd.equals("log") && tokens.size() 0){
        server.eventsLog(event.name + " @L" + String.valueOf(currline),tokens.get(0));
      } else if (cmd.equals("logprint") && tokens.size() > 0){
        server.eventsLog(event.name + " @L" + String.valueOf(currline),tokens.get(0));
        System.out.println("L" + String.valueOf(currline) + "@" + event.name + " msg: " + tokens.get(0));
      } else if (cmd.equals("sleep") && tokens.size() > 0) {
        try {
          Thread.sleep(Integer.valueOf(tokens.get(0)));
        } catch (InterruptedException e) {
          if (stopping) {
            break;
          }
        }
      } else if (cmd.equals("teleport")) {
        teleport(tokens);
      } else if (cmd.equals("npc")) {
        npcSpawn(tokens);
      } else if (cmd.equals("npckill")) {
        npcKill(tokens);
      } else if (cmd.equals("say")) {
        say(tokens);
      } else if (cmd.equals("broadcast")) {
        broadcast(tokens);
      } else if (cmd.equals("execsvrcmd")) {
        execsvrcmd(tokens);
      } else if (cmd.equals("execcmd")) {
        execcmd(tokens);
      } else if (cmd.equals("set")) {
        set(tokens);
      } else if (cmd.equals("inc")) {
        increment(tokens);
      } else if (cmd.equals("dec")) {
        decrement(tokens);
      } else if (cmd.equals("push")) {
        push(tokens);
      } else if (cmd.equals("if")) {
        condition(tokens, actions);
      } else if (cmd.equals("else")) {
        currline = ifstack.remove(0);
      } else if (cmd.equals("while")) {
        loop(tokens, actions);
      } else if (cmd.equals("break")) {
        breakloop(tokens, actions);
      } else if (cmd.equals("endwhile") || cmd.equals("continue")) {
        currline = whilestack.remove(0);
      } else if (cmd.equals("run")) {
        cmdrun(tokens, false);
      } else if (cmd.equals("launch")) {
        cmdrun(tokens, true);
      } else {
        notifyError("Command not found!");
      }

      currline++;
    }

    // implicit return value -> empty array
    threadstack.add(0, PostfixEvaluator.fromArray(new ArrayList<String>()));

    // finished -> remove itself from the running thread list
    if (threadname != null) {
      eventHost.running.remove(threadname);
    }

  }

  private ArrayList<String> parseLine(String line) {
    ArrayList<String> tokens = new ArrayList<String>();

    String currtok = null;
    boolean inString = false;
    for (int i = 0; i < line.length(); i++) {
      if (!inString && String.valueOf(line.charAt(i)).matches("\\s")) {
        if (currtok != null) {
          tokens.add(currtok);
          currtok = null;
        }
        continue;
      }

      if (currtok == null) {
        currtok = "";
      }

      if (inString && line.charAt(i) == '\\') { // Escape character
        i++;
        currtok += line.charAt(i);
        continue;
      }

      if (line.charAt(i) == '"') {
        inString = !inString;
        continue;
      }

      currtok += line.charAt(i);
    }

    if (currtok != null) {
      tokens.add(currtok);
    }

    return tokens;
  }

  private void evaluateVariables(ArrayList<String> tokens) {
    for (int i = 0; i < tokens.size(); i++) {
      String var = evaluateVar(tokens.get(i));
      if (var != null) {
        tokens.set(i, var);
      }
    }
  }

  protected String evaluateVar(String varname) {
    if (varname == null || varname.equals("")) {
      return "";
    }

    if (varname.charAt(0) == REFERENCEOP) { // escape variable
      char c = varname.charAt(1);
      if (c == LOCALSCOPE || c == PLAYERSCOPE || c == GLOBALSCOPE) {
        return varname.substring(1);
      } else {
        return null; // not a variable
      }
    } else if (varname.charAt(0) == LOCALSCOPE) { // local var
      String loc = varname.substring(1);
      // check special vars
      if (loc.equals("PLAYER")) {
        return player != null ? player.getName() : "null";
      } else if (loc.equals("EVENT")) {
        return event.name;
      } else if (loc.equals("THIS")) {
        return "$" + event.name;
      } else if (loc.equals("VALUE")) {
        return eventHost.globals.get(event.name);
      } else if (loc.equals("COORD")) {
        return String.valueOf(event.coordinate);
      } else if (loc.equals("POP")) {
        return threadstack.size() == 0 ? "null" : threadstack.remove(0);
      } else if (loc.equals("TOP")) {
        return threadstack.size() == 0 ? "null" : threadstack.get(0);
      } else if (eventHost.colors.containsKey(loc)) {
        return "\u00a7" + eventHost.colors.get(loc);
      } else {
        String v = vars.get(loc);
        if (v == null) {
          vars.put(loc, "null");
          v = "null";
        }
        return String.valueOf(v);
      }

    } else if (varname.charAt(0) == PLAYERSCOPE) { // player var
      return String.valueOf(player.vars.get(varname.substring(1)));

    } else if (varname.charAt(0) == GLOBALSCOPE) { // global perm var (event
                                                   // value)
      String name = varname.substring(1);
      if (eventHost.globals.containsKey(name)) {
        String ret = eventHost.globals.get(name);
        return ret;
      } else {
        notifyError("Event " + name + " not found!");
        return "null";
      }
    }

    return null; // not a variable
  }

  /*---- Interaction ----*/

  private void say(ArrayList<String> tokens) {
    if (tokens.size() < 2) {
      notifyError("Wrong number of arguments!");
      return;
    }

    Player p = server.findPlayer(tokens.remove(0));
    if (p == null) {
      notifyError("Player not found!");
      return;
    }

    String message = new PostfixEvaluator(this).evaluateSingle(tokens);
    p.addTMessage(message);
  }

  private void broadcast(ArrayList<String> tokens) {
    String message = new PostfixEvaluator(this).evaluateSingle(tokens);
    server.runCommand("say", message);
  }

  private void teleport(ArrayList<String> tokens) {
    if (tokens.size() < 2) {
      notifyError("Wrong number of arguments!");
      return;
    }

    Player p = server.findPlayer(tokens.get(0));
    if (p == null) {
      notifyError("Source player not online!");
      return;
    }

    Coordinate c = null;

    String dest = tokens.get(1);

    // Try to find such a player
    Player q = server.findPlayer(dest);
    if (q != null) {
      c = q.position.coordinate();
    }

    // Try to find such an event
    if (c == null && dest.length() > 0 && dest.charAt(0) == GLOBALSCOPE) {
      Event evdest = eventHost.findEvent(dest.substring(1));
      if (evdest != null) {
        c = evdest.coordinate;
      }
    }

    if (c == null) { // try to find a warp with that name
      String w = server.data.warp.getName(dest);
      if (w != null) {
        p.teleportSelf(server.data.warp.get(w)); // port to warp, ignore rest
        return;
      }
    }

    // try to parse as coordinate
    if (c == null) {
      c = Coordinate.fromString(dest);
    }

    if (c != null) {
      p.teleportSelf(c);
    } else {
      notifyError("Destination not found!");
    }
  }

  private void execcmd(ArrayList<String> tokens) {
    if (tokens.size() == 0) {
      notifyError("Wrong number of arguments!");
      return; // no command to execute
    }

    Player p = server.findPlayer(tokens.remove(0));
    if (p == null) {
      notifyError("Player not found!");
      return;
    }

    String message = server.options.getBoolean("useSlashes") ? "/" : "!";
    for (String token : tokens) {
      message += token + " ";
    }
    message = message.substring(0, message.length() - 1);

    // execute the server command, overriding the player permissions
    p.parseCommand(message, true);

    // handle forwarding
    String cmd = tokens.get(0);
    PlayerCommand command = server.resolvePlayerCommand(cmd, p.getGroup());

    // forwarding if necessary
    CommandConfig config = server.config.commands.getTopConfig(cmd);
    if ((command instanceof ExternalCommand)
        || (config != null && config.forwarding != Forwarding.NONE)
        || server.config.properties.getBoolean("forwardAllCommands")) {
      p.forwardMessage(message);
    }

  }

  private void execsvrcmd(ArrayList<String> tokens) {
    if (tokens.size() == 0) {
      notifyError("Wrong number of arguments!");
      return; // no command to execute
    }

    String cmd = tokens.get(0);
    String args = "";
    for (String t : tokens) {
      args += t + " ";
    }
    args = args.substring(0, args.length() - 1);

    ServerCommand command = server.getCommandParser().getServerCommand(cmd);
    if (command != null) {
      server.runCommand(cmd, args);
    }

  }

  private void npcSpawn(ArrayList<String> tokens) {
    if (tokens.size() < 5) {
      notifyError("Wrong number of arguments!");
      return;
    }

    String name = tokens.remove(0);
    if (eventHost.npcs.get(name) != null) {
      notifyError("An NPC with this name still exists!");
      return;
    }

    Event event = eventHost.findEvent(tokens.remove(0));
    if (event == null) {
      notifyError("Event associated with NPC not found!");
      return;
    }

    int x = 0;
    int y = 0;
    int z = 0;
    try {
      x = Integer.valueOf(tokens.remove(0));
      y = Integer.valueOf(tokens.remove(0));
      z = Integer.valueOf(tokens.remove(0));
    } catch (Exception e) {
      notifyError("Invalid NPC spawn coordinate!");
      return;
    }
    Coordinate c = new Coordinate(x, y, z);

    try {
      NpcBot s = new NpcBot(name, event.name, server, c);
      server.bots.connect(s);
      eventHost.npcs.put(name, s);
    } catch (IOException ex) {
      notifyError("Could not spawn NPC!");
      ex.printStackTrace();
    } catch (ConnectException e) {
      notifyError("Could not spawn NPC!");
      e.printStackTrace();
    }
  }

  private void npcKill(ArrayList<String> tokens) {
    if (tokens.size() < 1) {
      notifyError("Wrong number of arguments!");
      return;
    }

    String name = new PostfixEvaluator(this).evaluateSingle(tokens);
    NpcBot b = eventHost.npcs.remove(name);
    if (b == null) {
      notifyError("An NPC with this name is not logged on!");
      return;
    }

    try {
      b.logout();
    } catch (IOException e) {
      notifyError("Error while logging out bot!");
      e.printStackTrace();
    }
  }

  /*---- variable & runtime ----*/

  private void set(ArrayList<String> tokens) {
    if (tokens.size() < 2) {
      notifyError("Wrong number of arguments!");
      return;
    }

    ArrayList<String> vals = new PostfixEvaluator(this).evaluate(tokens);

    if (vals == null || vals.size() < 2) {
      return;
    }

    /* only 2 elements left - normal behaviour like before */
    if (vals.size() == 2) {
      String s = vals.remove(0);
      String v = vals.remove(0);
      assignVariable(s, v);
    } else if (vals.size() > 2) { /* more elements = multiple assignment */
      /* separating symbols and values (looking for "to" keyword */
      ArrayList<String> vars = new ArrayList<String>();
      while (vals.size() > 0 && !vals.get(0).equals("to")) {
        vars.add(vals.remove(0));
      }

      if (vals.size() == 0) {
        notifyError("Invalid multiple assignment / invalid expression! ('to' keyword missing?)");
        return;
      }

      vals.remove(0); // remove 'to'

      /* execute multiple assignment */
      while (vars.size() != 0) {
        String s = vars.remove(0);
        String v = "null";

        if (vals.size() != 0) {
          if (vars.size() != 0 || vals.size() == 1) {
            v = vals.remove(0);
          } else {
            v = PostfixEvaluator.fromArray(vals);
          }
        }

        assignVariable(s, v);
      }

    }
  }

  private void increment(ArrayList<String> tokens) {
    if (tokens.size() != 1) {
      notifyError("Wrong number of arguments!");
      return;
    }
    String v = tokens.get(0);
    assignVariable(v, String.valueOf(PostfixEvaluator.toNum(evaluateVar(v)) + 1));
  }

  private void decrement(ArrayList<String> tokens) {
    if (tokens.size() != 1) {
      notifyError("Wrong number of arguments!");
      return;
    }
    String v = tokens.get(0);
    assignVariable(v, String.valueOf(PostfixEvaluator.toNum(evaluateVar(v)) - 1));
  }

  private void assignVariable(String symbol, String value) {
    if (symbol == null || symbol.length() < 2) {
      return;
    }

    if (value == null) {
      value = "null";
    }

    char scope = symbol.charAt(0);
    String var = symbol.substring(1);

    if (scope == LOCALSCOPE) {
      vars.put(var, value);
    } else if (scope == PLAYERSCOPE) {
      player.vars.put(var, value);
    } else if (scope == GLOBALSCOPE) {
      if (eventHost.globals.containsKey(var)) {
        eventHost.globals.put(var, value);
      } else {
        notifyError("Assignment failed: event " + var + " not found!");
      }
    } else {
      notifyError("Assignment failed: Invalid variable reference!");
    }
  }

  private void push(ArrayList<String> tokens) {
    String exp = new PostfixEvaluator(this).evaluateSingle(tokens);

    if (exp == null) {
      threadstack.add(0, "null");
      return;
    }

    threadstack.add(0, exp);
  }

  private void cmdreturn(ArrayList<String> tokens) {
    threadstack.add(0, PostfixEvaluator.fromArray(new PostfixEvaluator(this).evaluate(tokens)));
  }

  private void cmdrun(ArrayList<String> tokens, boolean newThread) {
    if (tokens.size() < 1) {
      notifyError("Wrong number of arguments!");
      return;
    }

    Event e = eventHost.findEvent(tokens.remove(0));

    if (e == null) {
      notifyError("Event to run not found!");
      return;
    }

    String args = PostfixEvaluator.fromArray(new PostfixEvaluator(this).evaluate(tokens));

    if (!newThread) {
      if (stackdepth < MAXDEPTH) {
        threadstack.add(0, args);
        (new RunningEvent(eventHost, null, e, player, stackdepth + 1, threadstack)).run();
      } else {
        notifyError("Can not run event - stack level too deep!");
      }
    } else { // run in a new thread (passing threadstack copy!)
      @SuppressWarnings("unchecked")
      ArrayList<String> clone = (ArrayList<String>) threadstack.clone();
      clone.add(args);
      eventHost.executeEvent(e, player, clone);
    }
  }

  private void condition(ArrayList<String> tokens, ArrayList<String> actions) {
    boolean result = new PostfixEvaluator(this).evaluateCondition(tokens);

    int ifcounter = 0;

    boolean hasElse = false;
    if (result) {
      for (int i = currline + 1; i < actions.size(); i++) {
        ArrayList<String> line = parseLine(actions.get(i));
        if (line.size() == 0) {
          continue;
        }
        String cmd = line.get(0);

        if (cmd.equals("if")) {
          ifcounter++;
        }

        if (cmd.equals("else") && ifcounter == 0) {
          hasElse = true;
        }

        if (cmd.equals("endif")) {
          if (ifcounter == 0) {
            if (hasElse) {
              ifstack.add(0, i);
            }
            break;
          } else {
            ifcounter--;
          }
        }
      }
    } else {
      for (int i = currline + 1; i < actions.size(); i++) {
        ArrayList<String> line = parseLine(actions.get(i));
        if (line.size() == 0) {
          continue;
        }
        String cmd = line.get(0);

        if (cmd.equals("if")) {
          ifcounter++;
        }

        if (cmd.equals("endif")) {
          if (ifcounter == 0) { // no else found -> jump here
            currline = i;
            break;
          }
          ifcounter--;
        }

        if (cmd.equals("else") && ifcounter == 0) { // jump to else part
          currline = i;
          break;
        }
      }
    }
  }

  private void loop(ArrayList<String> tokens, ArrayList<String> actions) {
    boolean result = new PostfixEvaluator(this).evaluateCondition(tokens);

    if (!result) { // jump over while loop -> leave
      breakloop(tokens, actions);
    } else { // save current line for jump back -> enter
      whilestack.add(0, currline - 1);
    }
  }

  private void breakloop(ArrayList<String> tokens, ArrayList<String> actions) {
    int whilecounter = 0;
    for (int i = currline + 1; i < actions.size(); i++) {
      ArrayList<String> line = parseLine(actions.get(i));
      if (line.size() == 0) {
        continue;
      }
      String cmd = line.get(0);

      if (cmd.equals("while")) {
        whilecounter++;
      }

      if (cmd.equals("endwhile")) {
        if (whilecounter == 0) {
          currline = i;
          break;
        } else {
          whilecounter--;
        }
      }
    }
  }

  /*--------*/

  protected void notifyError(String err) {
    System.out.println("Error at L" + String.valueOf(currline) + "@" + event.name + ": " + lastline);
    System.out.println(err + "\n");
  }

}
TOP

Related Classes of simpleserver.events.RunningEvent

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.