Package lineage2.gameserver.scripts

Source Code of lineage2.gameserver.scripts.Scripts$ScriptClassAndMethod

/*
* 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 lineage2.gameserver.scripts;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import lineage2.commons.compiler.Compiler;
import lineage2.commons.compiler.MemoryClassLoader;
import lineage2.gameserver.Config;
import lineage2.gameserver.model.Player;
import lineage2.gameserver.model.quest.Quest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Mobius
* @version $Revision: 1.0 $
*/
public class Scripts
{
  /**
   * Field _log.
   */
  private static final Logger _log = LoggerFactory.getLogger(Scripts.class);
 
  /**
   * Field _instance.
   */
  private static final Scripts _instance = new Scripts();
 
  /**
   * Method getInstance.
   * @return Scripts
   */
  public static final Scripts getInstance()
  {
    return _instance;
  }
 
  /**
   * Field dialogAppends.
   */
  public static final Map<Integer, List<ScriptClassAndMethod>> dialogAppends = new HashMap<>();
  /**
   * Field onAction.
   */
  public static final Map<String, ScriptClassAndMethod> onAction = new HashMap<>();
  /**
   * Field onActionShift.
   */
  public static final Map<String, ScriptClassAndMethod> onActionShift = new HashMap<>();
 
  /**
   * Field compiler.
   */
  private final Compiler compiler = new Compiler();
  /**
   * Field _classes.
   */
  private final Map<String, Class<?>> _classes = new TreeMap<>();
 
  /**
   * Constructor for Scripts.
   */
  private Scripts()
  {
    load();
  }
 
  /**
   * Method load.
   */
  private void load()
  {
    _log.info("Scripts: Loading...");
   
    List<Class<?>> classes = new ArrayList<>();
   
    boolean result = false;
   
    File f = new File("../libs/lineage2-scripts.jar");
    if (f.exists())
    {
      JarInputStream stream = null;
      try
      {
        stream = new JarInputStream(new FileInputStream(f));
        JarEntry entry = null;
        while ((entry = stream.getNextJarEntry()) != null)
        {
          if (entry.getName().contains(ClassUtils.INNER_CLASS_SEPARATOR) || !entry.getName().endsWith(".class"))
          {
            continue;
          }
         
          String name = entry.getName().replace(".class", "").replace("/", ".");
         
          Class<?> clazz = Class.forName(name);
          if (Modifier.isAbstract(clazz.getModifiers()))
          {
            continue;
          }
          classes.add(clazz);
        }
        result = true;
      }
      catch (Exception e)
      {
        _log.error("Fail to load scripts.jar!", e);
        classes.clear();
      }
      finally
      {
        IOUtils.closeQuietly(stream);
      }
    }
   
    if (!result)
    {
      result = load(classes, "");
    }
   
    if (!result)
    {
      _log.error("Scripts: Failed loading scripts!");
      Runtime.getRuntime().exit(0);
      return;
    }
   
    _log.info("Scripts: Loaded " + classes.size() + " classes.");
   
    Class<?> clazz;
    for (int i = 0; i < classes.size(); i++)
    {
      clazz = classes.get(i);
      _classes.put(clazz.getName(), clazz);
    }
  }
 
  /**
   * Method init.
   */
  public void init()
  {
    for (Class<?> clazz : _classes.values())
    {
      addHandlers(clazz);
     
      if (Config.DONTLOADQUEST)
      {
        if (ClassUtils.isAssignable(clazz, Quest.class))
        {
          continue;
        }
      }
     
      if (ClassUtils.isAssignable(clazz, ScriptFile.class))
      {
        try
        {
          ((ScriptFile) clazz.newInstance()).onLoad();
        }
        catch (Exception e)
        {
          _log.error("Scripts: Failed running " + clazz.getName() + ".onLoad()", e);
        }
      }
    }
  }
 
  /**
   * Method reload.
   * @return boolean
   */
  public boolean reload()
  {
    _log.info("Scripts: Reloading...");
   
    return reload("");
  }
 
  /**
   * Method reload.
   * @param target String
   * @return boolean
   */
  public boolean reload(String target)
  {
    List<Class<?>> classes = new ArrayList<>();
   
    if (load(classes, target))
    {
      _log.info("Scripts: Reloaded " + classes.size() + " classes.");
    }
    else
    {
      _log.error("Scripts: Failed reloading script(s): " + target + "!");
      return false;
    }
   
    Class<?> clazz, prevClazz;
    for (int i = 0; i < classes.size(); i++)
    {
      clazz = classes.get(i);
     
      prevClazz = _classes.put(clazz.getName(), clazz);
      if (prevClazz != null)
      {
        if (ClassUtils.isAssignable(prevClazz, ScriptFile.class))
        {
          try
          {
            ((ScriptFile) prevClazz.newInstance()).onReload();
          }
          catch (Exception e)
          {
            _log.error("Scripts: Failed running " + prevClazz.getName() + ".onReload()", e);
          }
        }
       
        removeHandlers(prevClazz);
      }
     
      if (Config.DONTLOADQUEST)
      {
        if (ClassUtils.isAssignable(clazz, Quest.class))
        {
          continue;
        }
      }
     
      if (ClassUtils.isAssignable(clazz, ScriptFile.class))
      {
        try
        {
          ((ScriptFile) clazz.newInstance()).onLoad();
        }
        catch (Exception e)
        {
          _log.error("Scripts: Failed running " + clazz.getName() + ".onLoad()", e);
        }
      }
     
      addHandlers(clazz);
    }
   
    return true;
  }
 
  /**
   * Method shutdown.
   */
  public void shutdown()
  {
    for (Class<?> clazz : _classes.values())
    {
      if (ClassUtils.isAssignable(clazz, Quest.class))
      {
        continue;
      }
     
      if (ClassUtils.isAssignable(clazz, ScriptFile.class))
      {
        try
        {
          ((ScriptFile) clazz.newInstance()).onShutdown();
        }
        catch (Exception e)
        {
          _log.error("Scripts: Failed running " + clazz.getName() + ".onShutdown()", e);
        }
      }
    }
  }
 
  /**
   * Method load.
   * @param classes List<Class<?>>
   * @param target String
   * @return boolean
   */
  private boolean load(List<Class<?>> classes, String target)
  {
    Collection<File> scriptFiles = Collections.emptyList();
   
    File file = new File(Config.DATAPACK_ROOT, "data/scripts/" + target.replace(".", "/") + ".java");
    if (file.isFile())
    {
      scriptFiles = new ArrayList<>(1);
      scriptFiles.add(file);
    }
    else
    {
      file = new File(Config.DATAPACK_ROOT, "data/scripts/" + target);
      if (file.isDirectory())
      {
        scriptFiles = FileUtils.listFiles(file, FileFilterUtils.suffixFileFilter(".java"), FileFilterUtils.directoryFileFilter());
      }
    }
   
    if (scriptFiles.isEmpty())
    {
      return false;
    }
   
    Class<?> clazz;
    boolean success = compiler.compile(scriptFiles);
    if (success)
    {
      MemoryClassLoader classLoader = compiler.getClassLoader();
      for (String name : classLoader.getLoadedClasses())
      {
        if (name.contains(ClassUtils.INNER_CLASS_SEPARATOR))
        {
          continue;
        }
       
        try
        {
          clazz = classLoader.loadClass(name);
          if (Modifier.isAbstract(clazz.getModifiers()))
          {
            continue;
          }
          classes.add(clazz);
        }
        catch (ClassNotFoundException e)
        {
          success = false;
          _log.error("Scripts: Can't load script class: " + name, e);
        }
      }
      classLoader.clear();
    }
   
    return success;
  }
 
  /**
   * Method addHandlers.
   * @param clazz Class<?>
   */
  private void addHandlers(Class<?> clazz)
  {
    try
    {
      for (Method method : clazz.getMethods())
      {
        if (method.getName().contains("DialogAppend_"))
        {
          Integer id = Integer.parseInt(method.getName().substring(13));
          List<ScriptClassAndMethod> handlers = dialogAppends.get(id);
          if (handlers == null)
          {
            handlers = new ArrayList<>();
            dialogAppends.put(id, handlers);
          }
          handlers.add(new ScriptClassAndMethod(clazz.getName(), method.getName()));
        }
        else if (method.getName().contains("OnAction_"))
        {
          String name = method.getName().substring(9);
          onAction.put(name, new ScriptClassAndMethod(clazz.getName(), method.getName()));
        }
        else if (method.getName().contains("OnActionShift_"))
        {
          String name = method.getName().substring(14);
          onActionShift.put(name, new ScriptClassAndMethod(clazz.getName(), method.getName()));
        }
      }
    }
    catch (Exception e)
    {
      _log.error("", e);
    }
  }
 
  /**
   * Method removeHandlers.
   * @param script Class<?>
   */
  private void removeHandlers(Class<?> script)
  {
    try
    {
      for (List<ScriptClassAndMethod> entry : dialogAppends.values())
      {
        List<ScriptClassAndMethod> toRemove = new ArrayList<>();
        for (ScriptClassAndMethod sc : entry)
        {
          if (sc.className.equals(script.getName()))
          {
            toRemove.add(sc);
          }
        }
        for (ScriptClassAndMethod sc : toRemove)
        {
          entry.remove(sc);
        }
      }
     
      List<String> toRemove = new ArrayList<>();
      for (Map.Entry<String, ScriptClassAndMethod> entry : onAction.entrySet())
      {
        if (entry.getValue().className.equals(script.getName()))
        {
          toRemove.add(entry.getKey());
        }
      }
      for (String key : toRemove)
      {
        onAction.remove(key);
      }
     
      toRemove = new ArrayList<>();
      for (Map.Entry<String, ScriptClassAndMethod> entry : onActionShift.entrySet())
      {
        if (entry.getValue().className.equals(script.getName()))
        {
          toRemove.add(entry.getKey());
        }
      }
      for (String key : toRemove)
      {
        onActionShift.remove(key);
      }
    }
    catch (Exception e)
    {
      _log.error("", e);
    }
  }
 
  /**
   * Method callScripts.
   * @param className String
   * @param methodName String
   * @return Object
   */
  public Object callScripts(String className, String methodName)
  {
    return callScripts(null, className, methodName, null, null);
  }
 
  /**
   * Method callScripts.
   * @param className String
   * @param methodName String
   * @param args Object[]
   * @return Object
   */
  public Object callScripts(String className, String methodName, Object[] args)
  {
    return callScripts(null, className, methodName, args, null);
  }
 
  /**
   * Method callScripts.
   * @param className String
   * @param methodName String
   * @param variables Map<String,Object>
   * @return Object
   */
  public Object callScripts(String className, String methodName, Map<String, Object> variables)
  {
    return callScripts(null, className, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, variables);
  }
 
  /**
   * Method callScripts.
   * @param className String
   * @param methodName String
   * @param args Object[]
   * @param variables Map<String,Object>
   * @return Object
   */
  public Object callScripts(String className, String methodName, Object[] args, Map<String, Object> variables)
  {
    return callScripts(null, className, methodName, args, variables);
  }
 
  /**
   * Method callScripts.
   * @param caller Player
   * @param className String
   * @param methodName String
   * @return Object
   */
  public Object callScripts(Player caller, String className, String methodName)
  {
    return callScripts(caller, className, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
  }
 
  /**
   * Method callScripts.
   * @param caller Player
   * @param className String
   * @param methodName String
   * @param args Object[]
   * @return Object
   */
  public Object callScripts(Player caller, String className, String methodName, Object[] args)
  {
    return callScripts(caller, className, methodName, args, null);
  }
 
  /**
   * Method callScripts.
   * @param caller Player
   * @param className String
   * @param methodName String
   * @param variables Map<String,Object>
   * @return Object
   */
  public Object callScripts(Player caller, String className, String methodName, Map<String, Object> variables)
  {
    return callScripts(caller, className, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, variables);
  }
 
  /**
   * Method callScripts.
   * @param caller Player
   * @param className String
   * @param methodName String
   * @param args Object[]
   * @param variables Map<String,Object>
   * @return Object
   */
  public Object callScripts(Player caller, String className, String methodName, Object[] args, Map<String, Object> variables)
  {
    Object o;
    Class<?> clazz;
   
    clazz = _classes.get(className);
    if (clazz == null)
    {
      _log.error("Script class " + className + " not found!");
      return null;
    }
   
    try
    {
      o = clazz.newInstance();
    }
    catch (Exception e)
    {
      _log.error("Scripts: Failed creating instance of " + clazz.getName(), e);
      return null;
    }
   
    if ((variables != null) && !variables.isEmpty())
    {
      for (Map.Entry<String, Object> param : variables.entrySet())
      {
        try
        {
          FieldUtils.writeField(o, param.getKey(), param.getValue());
        }
        catch (Exception e)
        {
          _log.error("Scripts: Failed setting fields for " + clazz.getName(), e);
        }
      }
    }
   
    if (caller != null)
    {
      try
      {
        Field field = null;
        if ((field = FieldUtils.getField(clazz, "self")) != null)
        {
          FieldUtils.writeField(field, o, caller.getRef());
        }
      }
      catch (Exception e)
      {
        _log.error("Scripts: Failed setting field for " + clazz.getName(), e);
      }
    }
   
    Object ret = null;
    try
    {
      Class<?>[] parameterTypes = new Class<?>[args.length];
      for (int i = 0; i < args.length; i++)
      {
        parameterTypes[i] = args[i] != null ? args[i].getClass() : null;
      }
     
      ret = MethodUtils.invokeMethod(o, methodName, args, parameterTypes);
    }
    catch (NoSuchMethodException nsme)
    {
      _log.error("Scripts: No such method " + clazz.getName() + "." + methodName + "()!");
    }
    catch (InvocationTargetException ite)
    {
      _log.error("Scripts: Error while calling " + clazz.getName() + "." + methodName + "()", ite.getTargetException());
    }
    catch (Exception e)
    {
      _log.error("Scripts: Failed calling " + clazz.getName() + "." + methodName + "()", e);
    }
   
    return ret;
  }
 
  /**
   * Method getClasses.
   * @return Map<String,Class<?>>
   */
  public Map<String, Class<?>> getClasses()
  {
    return _classes;
  }
 
  /**
   * @author Mobius
   */
  public static class ScriptClassAndMethod
  {
    /**
     * Field className.
     */
    public final String className;
    /**
     * Field methodName.
     */
    public final String methodName;
   
    /**
     * Constructor for ScriptClassAndMethod.
     * @param className String
     * @param methodName String
     */
    public ScriptClassAndMethod(String className, String methodName)
    {
      this.className = className;
      this.methodName = methodName;
    }
  }
}
TOP

Related Classes of lineage2.gameserver.scripts.Scripts$ScriptClassAndMethod

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.