package jk_5.nailed.server.map.game.script;
import jk_5.nailed.api.GameMode;
import jk_5.nailed.api.chat.*;
import jk_5.nailed.api.mappack.filesystem.IMount;
import jk_5.nailed.api.scoreboard.DisplayType;
import jk_5.nailed.api.world.Difficulty;
import jk_5.nailed.api.world.WeatherType;
import jk_5.nailed.server.map.NailedMap;
import jk_5.nailed.server.map.game.NailedGameManager;
import jk_5.nailed.server.map.game.script.api.ScriptMapApi;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mozilla.javascript.*;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ScriptingEngine {
private static final Logger logger = LogManager.getLogger();
private final NailedGameManager manager;
private FileSystem fileSystem;
private Thread thread;
private Context context = null;
private ScriptableObject scope;
private Script script;
public ScriptingEngine(NailedGameManager manager) {
this.manager = manager;
}
public boolean start(){
if(fileSystem != null){
fileSystem.unload();
}
fileSystem = new FileSystem();
IMount mappackMount = manager.getMap().mappack().getMappackMount();
if(mappackMount == null){
return false;
}
logger.info("Initializing ScriptingEngine for " + manager.getMap().toString());
try{
fileSystem.mount("", manager.getMap().mappack().getMappackMount());
}catch(FileSystemException e){
throw new ScriptEngineException("Was not able to mount mappack scripts to the game engine", e);
}
try {
if(!fileSystem.exists("game.js")){
return false;
}
final InputStream gameScript = fileSystem.openForBinaryRead("game.js");
if(gameScript == null){
return false;
}
thread = newThread(new Runnable() {
@Override
public void run() {
context = Context.enter();
scope = context.initStandardObjects();
scope.delete("Packages");
scope.delete("getClass");
scope.delete("JavaAdapter");
scope.delete("JavaImporter");
scope.delete("Continuation");
scope.delete("java");
scope.delete("javax");
scope.delete("org");
scope.delete("com");
scope.delete("edu");
scope.delete("net");
scope.delete("eval");
try{
scope.put("map", scope, new NativeJavaObject(scope, new ScriptMapApi((NailedMap) manager.getMap(), context, scope), ScriptMapApi.class));
scope.put("sleep", scope, new NativeJavaMethod(Thread.class.getDeclaredMethod("sleep", Long.TYPE), "sleep"));
scope.put("BaseComponent", scope, new NativeJavaClass(scope, BaseComponent.class));
scope.put("ChatColor", scope, new NativeJavaClass(scope, ChatColor.class));
scope.put("ClickEvent", scope, new NativeJavaClass(scope, ClickEvent.class));
scope.put("ComponentBuilder", scope, new NativeJavaClass(scope, ComponentBuilder.class));
scope.put("HoverEvent", scope, new NativeJavaClass(scope, HoverEvent.class));
scope.put("TextComponent", scope, new NativeJavaClass(scope, TextComponent.class));
scope.put("TranslatableComponent", scope, new NativeJavaClass(scope, TranslatableComponent.class));
scope.put("WeatherType", scope, new NativeJavaClass(scope, WeatherType.class));
scope.put("GameMode", scope, new NativeJavaClass(scope, GameMode.class));
scope.put("Difficulty", scope, new NativeJavaClass(scope, Difficulty.class));
scope.put("DisplayType", scope, new NativeJavaClass(scope, DisplayType.class));
script = context.compileReader(new InputStreamReader(gameScript), "game.js", 1, null);
gameScript.close();
boolean success = false;
try{
script.exec(context, scope);
success = true;
}catch(Exception e){
manager.getMap().broadcastChatMessage(new ComponentBuilder("The script engine has crashed. The game will be stopped").color(ChatColor.RED).create());
logger.fatal("Exception while executing game script. Script engine crashed", e);
}finally{
manager.onEnded(success);
}
}catch(Exception e){
logger.error("Unknown error in gamescript", e);
}
}
});
thread.start();
return true;
}catch(FileSystemException e){
throw new ScriptEngineException("FileSystem exception", e);
}
}
public void kill(){
this.thread.stop();
this.fileSystem.unload();
this.fileSystem = null;
}
private Thread newThread(Runnable r){
Thread t = new Thread(r);
t.setName("ScriptEngine-" + manager.getMap().id());
t.setDaemon(true);
t.setPriority(3);
return t;
}
}