/*
* Copyright © 2004-2019 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server 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 .
*/
package com.l2jserver.gameserver.scripting;
import static com.l2jserver.gameserver.config.Config.SCRIPT_ROOT;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Map.Entry;
import javax.script.ScriptException;
import org.mdkt.compiler.InMemoryJavaCompiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.gameserver.config.Config;
/**
* Script engine manager.
* @author KenM
* @author Zoey76
*/
public final class ScriptEngineManager {
private static final Logger LOG = LoggerFactory.getLogger(ScriptEngineManager.class);
private static final String CLASS_PATH = SCRIPT_ROOT.getAbsolutePath() + System.getProperty("path.separator") + System.getProperty("java.class.path");
private static final String MAIN = "main";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Class>[] ARG_MAIN = new Class[] {
String[].class
};
private static final InMemoryJavaCompiler COMPILER = InMemoryJavaCompiler.newInstance() //
.useOptions("-classpath", CLASS_PATH) //
.ignoreWarnings();
public void executeScriptList(File list) throws Exception {
if (Config.NO_QUESTS) {
if (!Config.NO_HANDLERS) {
addSource(new File(SCRIPT_ROOT, "com/l2jserver/datapack/handlers/MasterHandler.java"));
LOG.info("Handlers loaded, all other scripts skipped!");
}
return;
}
if (list.isFile()) {
try (FileInputStream fis = new FileInputStream(list);
InputStreamReader isr = new InputStreamReader(fis);
LineNumberReader lnr = new LineNumberReader(isr)) {
String line;
while ((line = lnr.readLine()) != null) {
if (Config.NO_HANDLERS && line.contains("MasterHandler.java")) {
continue;
}
String[] parts = line.trim().split("#");
if ((parts.length > 0) && !parts[0].isEmpty() && (parts[0].charAt(0) != '#')) {
line = parts[0];
if (line.endsWith("/**")) {
line = line.substring(0, line.length() - 3);
} else if (line.endsWith("/*")) {
line = line.substring(0, line.length() - 2);
}
final File file = new File(SCRIPT_ROOT, line);
if (file.isDirectory() && parts[0].endsWith("/**")) {
executeAllScriptsInDirectory(file, true);
} else if (file.isDirectory() && parts[0].endsWith("/*")) {
executeAllScriptsInDirectory(file, false);
} else if (file.isFile()) {
addSource(file);
} else {
LOG.warn("Failed loading: ({}) @ {}:{} - Reason: doesnt exists or is not a file.", file.getCanonicalPath(), list.getName(), lnr.getLineNumber());
}
}
}
}
} else {
throw new IllegalArgumentException("Argument must be an file containing a list of scripts to be loaded");
}
final Map> classes = COMPILER.compileAll();
for (Entry> e : classes.entrySet()) {
runMain(e.getValue());
}
}
private void executeAllScriptsInDirectory(File dir, boolean recurseDown) {
if (dir.isDirectory()) {
final File[] files = dir.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isDirectory() && recurseDown) {
if (Config.VERBOSE_LOADING) {
LOG.info("Entering folder: {}", file.getName());
}
executeAllScriptsInDirectory(file, recurseDown);
} else if (file.isFile()) {
addSource(file);
}
}
} else {
throw new IllegalArgumentException("The argument directory either doesnt exists or is not an directory.");
}
}
public Class> compileScript(File file) {
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader reader = new BufferedReader(isr)) {
return COMPILER.compile(getClassForFile(file), readerToString(reader));
} catch (Exception ex) {
LOG.warn("Error executing script!", ex);
}
return null;
}
public void executeScript(File file) throws Exception {
final Class> clazz = compileScript(file);
runMain(clazz);
}
public void executeScript(String file) throws Exception {
executeScript(new File(SCRIPT_ROOT, file));
}
public void addSource(File file) {
if (Config.VERBOSE_LOADING) {
LOG.info("Loading Script: {}", file.getAbsolutePath());
}
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader reader = new BufferedReader(isr)) {
COMPILER.addSource(getClassForFile(file), readerToString(reader));
} catch (Exception ex) {
LOG.warn("Error executing script!", ex);
}
}
private static String getClassForFile(File script) {
final String path = script.getAbsolutePath();
final String scpPath = SCRIPT_ROOT.getAbsolutePath();
if (path.startsWith(scpPath)) {
final int idx = path.lastIndexOf('.');
return path.substring(scpPath.length() + 1, idx).replace('/', '.').replace('\\', '.');
}
return null;
}
private static void runMain(Class> clazz) throws Exception {
final boolean isPublicClazz = Modifier.isPublic(clazz.getModifiers());
final Method mainMethod = findMethod(clazz, MAIN, ARG_MAIN);
if (mainMethod != null) {
if (!isPublicClazz) {
mainMethod.setAccessible(true);
}
mainMethod.invoke(null, new Object[] {
EMPTY_STRING_ARRAY
});
}
}
private static String readerToString(Reader reader) throws ScriptException {
try (BufferedReader in = new BufferedReader(reader)) {
final StringBuilder result = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
result.append(line).append(System.lineSeparator());
}
return result.toString();
} catch (IOException ex) {
throw new ScriptException(ex);
}
}
private static Method findMethod(Class> clazz, String methodName, Class>[] args) {
try {
final Method mainMethod = clazz.getMethod(methodName, args);
final int modifiers = mainMethod.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
return mainMethod;
}
} catch (NoSuchMethodException ignored) {
}
return null;
}
public File getCurrentLoadingScript() {
return null;
}
public static ScriptEngineManager getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
protected static final ScriptEngineManager INSTANCE = new ScriptEngineManager();
}
}