diff --git a/SimpleCronClone/examples/events/playerFirstJoin.sce b/SimpleCronClone/examples/events/playerFirstJoin.sce
new file mode 100644
index 0000000..f905451
--- /dev/null
+++ b/SimpleCronClone/examples/events/playerFirstJoin.sce
@@ -0,0 +1 @@
+do say $0:::$1
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/playerJoin.sce b/SimpleCronClone/examples/events/playerJoin.sce
new file mode 100644
index 0000000..f905451
--- /dev/null
+++ b/SimpleCronClone/examples/events/playerJoin.sce
@@ -0,0 +1 @@
+do say $0:::$1
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/playerQuit.sce b/SimpleCronClone/examples/events/playerQuit.sce
new file mode 100644
index 0000000..f905451
--- /dev/null
+++ b/SimpleCronClone/examples/events/playerQuit.sce
@@ -0,0 +1 @@
+do say $0:::$1
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/playerTeleportWorld.sce b/SimpleCronClone/examples/events/playerTeleportWorld.sce
new file mode 100644
index 0000000..90091e3
--- /dev/null
+++ b/SimpleCronClone/examples/events/playerTeleportWorld.sce
@@ -0,0 +1 @@
+do say $0:::$1::$2::$3
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/serverEmpty.sce b/SimpleCronClone/examples/events/serverEmpty.sce
new file mode 100644
index 0000000..f905451
--- /dev/null
+++ b/SimpleCronClone/examples/events/serverEmpty.sce
@@ -0,0 +1 @@
+do say $0:::$1
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/serverNotEmpty.sce b/SimpleCronClone/examples/events/serverNotEmpty.sce
new file mode 100644
index 0000000..f905451
--- /dev/null
+++ b/SimpleCronClone/examples/events/serverNotEmpty.sce
@@ -0,0 +1 @@
+do say $0:::$1
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/worldEmpty.sce b/SimpleCronClone/examples/events/worldEmpty.sce
new file mode 100644
index 0000000..201da71
--- /dev/null
+++ b/SimpleCronClone/examples/events/worldEmpty.sce
@@ -0,0 +1 @@
+do say $0:::$1::$2
\ No newline at end of file
diff --git a/SimpleCronClone/examples/events/worldNotEmpty.sce b/SimpleCronClone/examples/events/worldNotEmpty.sce
new file mode 100644
index 0000000..201da71
--- /dev/null
+++ b/SimpleCronClone/examples/events/worldNotEmpty.sce
@@ -0,0 +1 @@
+do say $0:::$1::$2
\ No newline at end of file
diff --git a/SimpleCronClone/examples/tab.sce b/SimpleCronClone/examples/tab.sce
new file mode 100644
index 0000000..4b6d226
--- /dev/null
+++ b/SimpleCronClone/examples/tab.sce
@@ -0,0 +1,20 @@
+#list of event then file to run when that event occurs
+#PS: spaces in the file name are a bad idea mmkay?
+
+playerJoin events/playerJoin.sce
+
+playerFirstJoin events/playerFirstJoin.sce
+
+playerQuit events/playerQuit.sce
+
+
+serverEmpty events/serverEmpty.sce
+
+serverNotEmpty events/serverNotEmpty.sce
+
+
+playerTeleportWorld events/playerTeleportWorld.sce
+
+worldEmpty events/worldEmpty.sce
+
+worldNotEmpty events/worldNotEmpty.sce
\ No newline at end of file
diff --git a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/CronEngine.java b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/CronEngine.java
index c7ee953..2730a05 100644
--- a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/CronEngine.java
+++ b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/CronEngine.java
@@ -17,12 +17,9 @@
package org.bonsaimind.bukkitplugins.simplecronclone;
import it.sauronsoftware.cron4j.Scheduler;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
-import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
diff --git a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventEngine.java b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventEngine.java
new file mode 100644
index 0000000..e049064
--- /dev/null
+++ b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventEngine.java
@@ -0,0 +1,207 @@
+/*
+ * This file is part of SimpleCronClone.
+ *
+ * SimpleCronClone 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.
+ *
+ * SimpleCronClone 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 SimpleCronClone. If not, see .
+ */
+package org.bonsaimind.bukkitplugins.simplecronclone;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.bukkit.Server;
+
+/**
+ * This is the engine which does the heavy lifting and interfacing
+ */
+public final class EventEngine {
+
+ private static final String COMMENT_START = "#";
+
+ private File workingDir;
+ private Server server;
+ private Plugin plugin;
+ private Logger logger;
+
+
+ //strings of the filePaths to the .sce files
+ private ArrayList eventJoin;
+ private ArrayList eventFirstJoin;
+ private ArrayList eventQuit;
+
+ private ArrayList eventServerEmpty;
+ private ArrayList eventServerNotEmpty;
+
+ private ArrayList eventPlayerWorldMove;
+
+ private ArrayList eventWorldEmpty;
+ private ArrayList eventWorldNotEmpty;
+
+
+ private static final String EVENT_JOIN = "playerJoin";
+ private static final String EVENT_QUIT = "playerQuit";
+ private static final String EVENT_FIRST_JOIN = "playerFirstJoin";
+ private static final String EVENT_SERVER_EMPTY = "serverEmpty";
+ private static final String EVENT_SERVER_NOT_EMPTY = "serverNotEmpty";
+ private static final String EVENT_PLAYER_WORLD_MOVE = "playerTeleportWorld";
+ private static final String EVENT_WORLD_EMPTY = "worldEmpty";
+ private static final String EVENT_WORLD_NOT_EMPTY = "worldNotEmpty";
+
+ public EventEngine (Plugin _plugin, Server server, File workingDir) {
+ this.server = server;
+ this.workingDir = workingDir;
+ this.plugin = _plugin;
+ this.logger = plugin.getLogger();
+ }
+
+ public void start() {
+ // clear all the old stuff away
+ stop();
+ readTab();//TODO: when does this fail? what do we do if it does?
+
+
+ }
+
+ public void stop() {
+ eventJoin = new ArrayList();
+
+ eventFirstJoin = new ArrayList();
+ eventQuit = new ArrayList();
+
+ eventServerEmpty = new ArrayList();
+ eventServerNotEmpty = new ArrayList();
+
+ eventPlayerWorldMove = new ArrayList();
+
+ eventWorldEmpty = new ArrayList();
+ eventWorldNotEmpty = new ArrayList();
+ }
+
+
+ /**
+ * Reads the tab.sce (from the default location) and parses it.
+ * @return Returns true if reading and parsing was without incident.
+ */
+ protected boolean readTab() {
+ File tab = new File(workingDir, "tab.sce");
+
+ if (!tab.exists() || !tab.canRead()) {
+ logger.log(Level.WARNING, "{0} does not exist or is not accessible.", tab.getPath());
+ return false;
+ }
+
+ try {
+ for (String line : ScriptParser.getLines(tab)) {
+ if (!line.isEmpty() && !line.trim().startsWith(COMMENT_START) && line.trim().endsWith(".sce")) {
+ parseTabLine(line);
+ }
+ }
+
+ return true;
+ } catch (FileNotFoundException ex) {
+ logger.log(Level.WARNING, "tab.sce does not exists!");
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Failed to read tab.sce:\n{0}", ex.getMessage());
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse the given line and add it to the event runner.
+ * @param line The line form the tab.sce.
+ */
+ protected void parseTabLine(String line) {
+ line = line.trim();
+
+ String eventPart = line.substring(0, line.lastIndexOf(" ")).trim();
+ final String commandPart = line.substring(line.lastIndexOf(" ") + 1).trim();
+
+ if (eventPart.equalsIgnoreCase(EVENT_JOIN)){
+ eventJoin.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_FIRST_JOIN)){
+ eventFirstJoin.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_QUIT)){
+ eventQuit.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_SERVER_EMPTY)){
+ eventServerEmpty.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_SERVER_NOT_EMPTY)){
+ eventServerNotEmpty.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_PLAYER_WORLD_MOVE)){
+ eventPlayerWorldMove.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_WORLD_EMPTY)){
+ eventWorldEmpty.add(commandPart);
+ }else if(eventPart.equalsIgnoreCase(EVENT_WORLD_NOT_EMPTY)){
+ eventWorldNotEmpty.add(commandPart);
+ }else{
+ //TODO: do we want to raise an exception if the line fails parsing?
+ logger.warning(String.format("line failed parsing:'%s':eventPart:'%s':commandPart:'%s'",line,eventPart,commandPart));
+ return; //bypasses the next logging. Already logged that we failed this line.
+ }
+ //TODO: better name this logging output?
+ logger.info(String.format("SCE waiting: %s:::%s", eventPart,commandPart));
+ }
+ private void runEvents(ArrayList filesToCall,final String[] args){
+ for (final String filePath : filesToCall){
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run(){
+ ScriptParser.executeScript(server,logger,new File(workingDir,filePath),args);
+ }
+ });
+ t.start();
+ }
+ }
+ public void eventPlayerJoin(final String player){
+ final String[] args = {EVENT_JOIN,player};
+ runEvents(eventJoin, args);
+ }
+ public void eventFirstJoin(String player) {
+ final String[] args = {EVENT_FIRST_JOIN,player};
+ runEvents(eventFirstJoin, args);
+ }
+
+ public void eventPlayerQuit(String player) {
+ final String[] args = {EVENT_QUIT,player};
+ runEvents(eventQuit, args);
+ }
+
+ public void eventPlayerWorldMove(String player, String from, String to) {
+ final String[] args = {EVENT_PLAYER_WORLD_MOVE,player,from,to};
+ runEvents(eventPlayerWorldMove, args);
+ }
+
+ public void eventWorldEmpty(String player, String world) {
+ final String[] args = {EVENT_WORLD_EMPTY,player,world};
+ runEvents(eventWorldEmpty, args);
+ }
+
+ public void eventWorldNotEmpty(String player, String world) {
+ final String[] args = {EVENT_WORLD_NOT_EMPTY,player,world};
+ runEvents(eventWorldNotEmpty, args);
+ }
+
+ public void eventServerNotEmpty(String player) {
+ final String[] args = {EVENT_SERVER_NOT_EMPTY,player};
+ runEvents(eventServerNotEmpty, args);
+ }
+
+ public void eventServerEmpty(String player) {
+ final String[] args = {EVENT_SERVER_EMPTY,player};
+ runEvents(eventServerEmpty, args);
+ }
+}
diff --git a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventListener.java b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventListener.java
new file mode 100644
index 0000000..0632035
--- /dev/null
+++ b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/EventListener.java
@@ -0,0 +1,57 @@
+package org.bonsaimind.bukkitplugins.simplecronclone;
+
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerChangedWorldEvent;
+
+public class EventListener implements Listener {
+ private Plugin sccMain;
+
+ public EventListener(Plugin plugin) {
+ sccMain = plugin;
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event){
+ //eventJoin
+ sccMain.eventEngine.eventPlayerJoin(event.getPlayer().getName());
+ if (!event.getPlayer().hasPlayedBefore()){
+ //eventFirstJoin
+ //TODO: this seems not to be reliable?
+ sccMain.eventEngine.eventFirstJoin(event.getPlayer().getName());
+ }
+ if (sccMain.getServer().getOnlinePlayers().length == 1){
+ //only user logged in means that we were just empty.
+ sccMain.eventEngine.eventServerNotEmpty(event.getPlayer().getName());
+ }
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ //eventQuit
+ sccMain.eventEngine.eventPlayerQuit(event.getPlayer().getName());
+ if (sccMain.getServer().getOnlinePlayers().length == 0){
+ //no users logged in, means last player just quit.
+ sccMain.eventEngine.eventServerEmpty(event.getPlayer().getName());
+ }
+ }
+
+ @EventHandler
+ public void onPlayerChangedWorld(PlayerChangedWorldEvent event){
+ //eventPlayerWorldMove
+ sccMain.eventEngine.eventPlayerWorldMove(event.getPlayer().getName(),event.getFrom().getName(),event.getPlayer().getWorld().getName());
+
+ if(event.getFrom().getPlayers().size()==0){
+ //eventWorldEmpty
+ sccMain.eventEngine.eventWorldEmpty(event.getPlayer().getName(),event.getFrom().getName());
+ }
+ if(event.getPlayer().getWorld().getPlayers().size() == 1){
+ //eventWorldNotEmpty (if one player, that must means its ours that just moved)
+ sccMain.eventEngine.eventWorldNotEmpty(event.getPlayer().getName(),event.getPlayer().getWorld().getName());
+ }
+
+ }
+}
diff --git a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/Plugin.java b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/Plugin.java
index 00044cd..a5ed708 100644
--- a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/Plugin.java
+++ b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/Plugin.java
@@ -35,11 +35,18 @@ public class Plugin extends JavaPlugin {
private Server server;
private CronEngine engine;
+ private EventListener eventListener;
+
+ //public so that eventListener calls into it via this.plugin.scriptEngine.$EVENT_NAME()
+ public EventEngine eventEngine;
@Override
public void onDisable() {
engine.stop();
engine = null;
+ eventListener = null;
+ eventEngine.stop();
+ eventEngine = null;
}
@Override
@@ -52,6 +59,12 @@ public void onEnable() {
engine = new CronEngine(server, getLogger(), new File("plugins/SimpleCronClone/"));
engine.start();
+ eventEngine = new EventEngine(this, server, new File("plugins/SimpleCronClone"));
+ eventEngine.start();
+
+ eventListener = new EventListener(this);
+ server.getPluginManager().registerEvents(eventListener, this);
+
setCommands();
try {
diff --git a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/ScriptParser.java b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/ScriptParser.java
index d32b11b..d95af75 100644
--- a/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/ScriptParser.java
+++ b/SimpleCronClone/src/org/bonsaimind/bukkitplugins/simplecronclone/ScriptParser.java
@@ -45,7 +45,8 @@ public final class ScriptParser {
private static final String COMMAND_EXEC = "exec";
private static final String COMMAND_EXECWAIT = "execWait";
private static final String COMMENT_START = "#";
- private static final String OUTPUT_TOKEN = "$?";
+ private static final String VARIABLE_START_TOKEN = "$";
+ private static final String OUTPUT_TOKEN = "?";
/**
* If you wonder what this is, no problem. I'll tell you.
* This is some awesome RegEx written by Tim Pietzcker
@@ -100,6 +101,56 @@ public static boolean executeScript(final Server server, final Logger logger, Fi
return true;
}
+ /**
+ * Parses and executes the given script.
+ * @param server
+ * @param logger
+ * @param script The file which represents the script.
+ * @param args array of arguments to replace within the script (eg replace "$1" with args[1]), arg[0] is event name
+ * @return Returns true of the execution was without incident.
+ */
+ public static boolean executeScript(final Server server, final Logger logger, File script, String[] args){
+ logger.log(Level.INFO, "Executing: {0}", script.getPath());
+
+ String lastOutput = "";
+
+ try {
+ for (String line : getLines(script)) {
+ if (!line.isEmpty() && !line.trim().startsWith(COMMENT_START)) {
+ // Remove inlined comments
+ if (line.contains(COMMENT_START)) {
+ line = line.substring(0, line.indexOf(COMMENT_START));
+ }
+
+ line = line.trim();
+
+ if (!line.isEmpty() && line.indexOf(' ') > 0) {
+ line = line.replace(VARIABLE_START_TOKEN+OUTPUT_TOKEN, lastOutput);
+ // replace $0,$1 ect... with what it actually is
+ //TODO: make it so that we ignore the VARIABLE_START_TOKEN if it is preceded with `\`
+ //or just do whatever string-escaping is needed, there should be a built-in for it right?
+ for (int i=0; i