me.mast3rplan.phantombot.script.Script.java Source code

Java tutorial

Introduction

Here is the source code for me.mast3rplan.phantombot.script.Script.java

Source

/*
 * Copyright (C) 2017 phantombot.tv
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package me.mast3rplan.phantombot.script;

import com.google.common.collect.Lists;

import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.mozilla.javascript.*;
import org.mozilla.javascript.tools.debugger.Main;
import me.mast3rplan.phantombot.PhantomBot;

public class Script {

    public static final NativeObject global = new NativeObject();
    @SuppressWarnings("rawtypes")
    private final List<ScriptDestroyable> destroyables = Lists.newArrayList();
    private final NativeObject vars = new NativeObject();
    private final ScriptFileWatcher fileWatcher;
    private final File file;
    private Context context;
    private boolean killed = false;
    private int fileNotFoundCount = 0;

    @SuppressWarnings("CallToThreadStartDuringObjectConstruction")
    public Script(File file) {

        if (PhantomBot.reloadScripts) {
            this.fileWatcher = new ScriptFileWatcher(this);
        } else {
            if (file.getPath().indexOf("/lang/") != -1) {
                this.fileWatcher = new ScriptFileWatcher(this);
            } else {
                this.fileWatcher = null;
            }
        }
        this.file = file;

        if (!file.getName().endsWith(".js") || file.getName().startsWith(".")) {
            return;
        }

        Thread.setDefaultUncaughtExceptionHandler(com.gmt2001.UncaughtExceptionHandler.instance());

        if (this.fileWatcher != null) {
            new Thread(fileWatcher).start();
        }
    }

    @SuppressWarnings("rawtypes")
    public void reload() throws IOException {
        if (killed) {
            return;
        }

        doDestroyables();
        try {
            load();
            if (file.getPath().endsWith("init.js")) {
                com.gmt2001.Console.out.println("Reloaded module: init.js");
            } else {
                String path = file.getPath().replace("\056\134", "").replace("\134", "/").replace("scripts/", "");
                com.gmt2001.Console.out.println("Reloaded module: " + path);
            }
            fileNotFoundCount = 0;
        } catch (Exception ex) {
            if (ex.getMessage().indexOf("This could be a caching issue") != -1) {
                fileNotFoundCount++;
                if (fileNotFoundCount == 1) {
                    return;
                }
            } else {
                fileNotFoundCount = 0;
            }

            if (file.getPath().endsWith("init.js")) {
                com.gmt2001.Console.err.println("Failed to reload module: init.js: " + ex.getMessage());
            } else {
                String path = file.getPath().replace("\056\134", "").replace("\134", "/").replace("scripts/", "");
                com.gmt2001.Console.err.println("Failed to reload module: " + path + ": " + ex.getMessage());
            }
        }
    }

    @SuppressWarnings("rawtypes")
    public void reload(Boolean silent) throws IOException {
        if (killed) {
            return;
        }

        doDestroyables();
        try {
            load();
            if (silent) {
                if (file.getPath().endsWith("init.js")) {
                    com.gmt2001.Console.out.println("Reloaded module: init.js");
                } else {
                    String path = file.getPath().replace("\056\134", "").replace("\134", "/").replace("scripts/",
                            "");
                    com.gmt2001.Console.out.println("Reloaded module: " + path);
                }
            }
            fileNotFoundCount = 0;
        } catch (Exception ex) {
            if (ex.getMessage().indexOf("This could be a caching issue") != -1) {
                fileNotFoundCount++;
                if (fileNotFoundCount == 1) {
                    return;
                }
            } else {
                fileNotFoundCount = 0;
            }

            if (file.getPath().endsWith("init.js")) {
                com.gmt2001.Console.err.println("Failed to reload module: init.js: " + ex.getMessage());
            } else {
                String path = file.getPath().replace("\056\134", "").replace("\134", "/").replace("scripts/", "");
                com.gmt2001.Console.err.println("Failed to reload module: " + path + ": " + ex.getMessage());
            }
        }
    }

    public void load() throws IOException {
        if (killed) {
            return;
        }

        if (!file.getName().endsWith(".js")) {
            return;
        }

        /* Enable Error() in JS to provide an object with fileName and lineNumber. */
        final ContextFactory ctxFactory = new ContextFactory() {
            @Override
            protected boolean hasFeature(Context cx, int featureIndex) {
                switch (featureIndex) {
                case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR:
                    return true;
                default:
                    return super.hasFeature(cx, featureIndex);
                }
            }
        };
        RhinoException.setStackStyle(StackStyle.MOZILLA);

        /* Create Debugger Instance - this opens for only init.js */
        Main debugger = null;
        if (PhantomBot.enableRhinoDebugger) {
            if (file.getName().endsWith("init.js")) {
                debugger = new Main(file.getName());
                debugger.attachTo(ctxFactory);
            }
        }

        context = ctxFactory.enterContext();
        if (!PhantomBot.enableRhinoDebugger) {
            context.setOptimizationLevel(9);
        }

        ScriptableObject scope = context.initStandardObjects(global, false);
        scope.defineProperty("$", global, 0);
        scope.defineProperty("$api", ScriptApi.instance(), 0);
        scope.defineProperty("$script", this, 0);
        scope.defineProperty("$var", vars, 0);

        /* Configure debugger. */
        if (PhantomBot.enableRhinoDebugger) {
            if (file.getName().endsWith("init.js")) {
                debugger.setBreakOnEnter(false);
                debugger.setScope(scope);
                debugger.setSize(640, 480);
                debugger.setVisible(true);
            }
        }

        try {
            context.evaluateString(scope, FileUtils.readFileToString(file), file.getName(), 1, null);
        } catch (FileNotFoundException ex) {
            throw new IOException("File not found. This could be a caching issue, will retry.");
        } catch (EvaluatorException ex) {
            throw new IOException("JavaScript Error: " + ex.getMessage());
        } catch (Exception ex) {
            throw new IOException(ex.getMessage());
        }
    }

    @SuppressWarnings("rawtypes")
    public List<ScriptDestroyable> destroyables() {
        return destroyables;
    }

    @SuppressWarnings("rawtypes")
    public void doDestroyables() {
        for (ScriptDestroyable destroyable : destroyables) {
            destroyable.destroy();
        }

        destroyables.clear();
    }

    public File getFile() {
        return file;
    }

    public String getPath() {
        return file.toPath().toString();
    }

    public Context getContext() {
        return context;
    }

    public boolean isKilled() {
        return killed;
    }

    public void kill() {
        ObservingDebugger od = new ObservingDebugger();
        context.setDebugger(od, 0);
        context.setGeneratingDebug(true);
        context.setOptimizationLevel(-1);
        doDestroyables();
        od.setDisconnected(true);
        killed = true;
    }
}