mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-01-17 00:05:07 -05:00
410 lines
12 KiB
Java
Executable File
410 lines
12 KiB
Java
Executable File
/*
|
|
Title: script_class_loader
|
|
Description: Responsible for loading script classes.
|
|
*/
|
|
|
|
package script;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.Hashtable;
|
|
import java.util.TreeSet;
|
|
|
|
|
|
public class script_class_loader extends ClassLoader
|
|
{
|
|
// keep a dummy method object around to identify methods that are missing from scripts
|
|
private static final Object NO_OBJECT = new Object();
|
|
public static Method NO_METHOD = null;
|
|
|
|
private String myClassName; // class this loader is responsible for
|
|
private Class myClass; // class this loader has loaded
|
|
private Object myObject; // object instance of our class
|
|
private Hashtable methods; // cached methods for the class
|
|
private ArrayList derivedClasses; // names of classes that derive from the class
|
|
|
|
private static Hashtable loaderCache = new Hashtable(2003); // class name -> class_loader map
|
|
private static TreeSet defaultLoad; // names of classes that should be loaded by the default loader
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @param name name of class this loader is responsible for
|
|
*/
|
|
private script_class_loader(String name) throws ClassNotFoundException
|
|
{
|
|
|
|
// initialize basic data
|
|
myClassName = name;
|
|
myClass = null;
|
|
myObject = null;
|
|
methods = new Hashtable(53);
|
|
derivedClasses = null;
|
|
|
|
loaderCache.put(name, this);
|
|
|
|
// load the class and create an instance of it
|
|
loadClass(name);
|
|
if (myClass != null)
|
|
{
|
|
try
|
|
{
|
|
myObject = myClass.getDeclaredConstructor().newInstance();
|
|
if (myObject == null)
|
|
{
|
|
System.err.println("WARNING: Java Error creating object for class " + name);
|
|
}
|
|
}
|
|
catch ( InstantiationException | IllegalAccessException err )
|
|
{
|
|
System.err.println("WARNING: Java Error creating class instance " + name + " : " + err);
|
|
} catch (NoSuchMethodException | InvocationTargetException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
else
|
|
System.err.println("WARNING: Java Error script_class_loader creating class " + name);
|
|
} // script_class_loader()
|
|
|
|
/**
|
|
* Finds the class loader for a given class. If the class loader doesn't
|
|
* exist, creates it.
|
|
*
|
|
* @param name name of class we want the loader for
|
|
*/
|
|
public static script_class_loader getClassLoader(String name) throws ClassNotFoundException
|
|
{
|
|
Object test = loaderCache.get(name);
|
|
if (test != null)
|
|
return (script_class_loader)test;
|
|
|
|
if (!name.startsWith("script."))
|
|
{
|
|
ClassNotFoundException err = new ClassNotFoundException("Class " + name + " does not start with 'script.'");
|
|
err.printStackTrace();
|
|
throw err;
|
|
}
|
|
|
|
script_class_loader loader = new script_class_loader(name);
|
|
if (loader != null && (loader.getMyClass() == null || loader.getMyObject() == null))
|
|
loader = null;
|
|
return loader;
|
|
} // getClassLoader()
|
|
|
|
/**
|
|
* Removes a loader for a given script, so the class can be loaded again
|
|
*
|
|
* @param name name of class whose loader we want to remove
|
|
*
|
|
* @return true if the class was unloaded, false if the class doesn't exist
|
|
*/
|
|
public static boolean unloadClass(String name)
|
|
{
|
|
|
|
String flag = base_class.getConfigSetting("GameServer", "scriptListLoadUnload");
|
|
|
|
if (!(flag == null || flag.length() < 1))
|
|
{
|
|
System.out.println("script_class_loader unloadClass " + name);
|
|
}
|
|
|
|
boolean result = false;
|
|
if (loaderCache.containsKey(name))
|
|
{
|
|
script_class_loader loader = (script_class_loader)loaderCache.remove(name);
|
|
// unload all the classes that derive from this one
|
|
if (loader.derivedClasses != null)
|
|
{
|
|
int count = loader.derivedClasses.size();
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
unloadClass((String)loader.derivedClasses.get(i));
|
|
}
|
|
loader.derivedClasses.clear();
|
|
loader.derivedClasses = null;
|
|
}
|
|
// unload the class methods
|
|
if (loader.methods != null)
|
|
{
|
|
loader.methods.clear();
|
|
loader.methods = null;
|
|
}
|
|
loader.myClass = null;
|
|
loader.myObject = null;
|
|
loader.myClassName = null;
|
|
loader = null;
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
// if the class file exists, go ahead and return true
|
|
String pathedName = name.replace('.', java.io.File.separatorChar);
|
|
String fullname = script_entry.getScriptPath() + pathedName + ".class";
|
|
File file = new File(fullname);
|
|
if (file != null && file.isFile()) {
|
|
result = true;
|
|
}
|
|
|
|
}
|
|
return result;
|
|
} // unloadClass()
|
|
|
|
/**
|
|
* Adds a derived class name to our derived classes list.
|
|
*
|
|
* @param className the derived class name
|
|
*/
|
|
public void addDerivedClass(String className)
|
|
{
|
|
if (derivedClasses == null)
|
|
derivedClasses = new ArrayList();
|
|
derivedClasses.add(className);
|
|
} // addDerivedClass()
|
|
|
|
/**
|
|
* Gets the cached class for this loader.
|
|
*
|
|
* @return the class
|
|
*/
|
|
public Class getMyClass()
|
|
{
|
|
return myClass;
|
|
} // getMyClass()
|
|
|
|
/**
|
|
* Gets the cached object for a class.
|
|
*
|
|
* @return the object
|
|
*/
|
|
public Object getMyObject()
|
|
{
|
|
return myObject;
|
|
} // getMyObject()
|
|
|
|
/**
|
|
* Gets the cached methods for a class.
|
|
*
|
|
* @return the methods hash table
|
|
*/
|
|
public Hashtable getMyMethods()
|
|
{
|
|
return methods;
|
|
} // getMyMethods()
|
|
|
|
/**
|
|
* Loads a class. If the class is the one this loader is responsible for,
|
|
* loads it, else passes the request to another loader.
|
|
*
|
|
* @param name class name
|
|
* @param resolve flag to resolve the class
|
|
*
|
|
* @return the class
|
|
*/
|
|
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException
|
|
{
|
|
// filter out classes that will be loaded by the default loader
|
|
int nameLength = name.length();
|
|
if ((nameLength > 5 && name.startsWith("java.")) ||
|
|
(nameLength > 4 && name.startsWith("sun.")) ||
|
|
(nameLength > 10 && name.startsWith("intuitive.")) ||
|
|
(nameLength > 4 && name.startsWith("jdk.")) ||
|
|
defaultLoad.contains(name))
|
|
{
|
|
Class cls = super.loadClass(name, resolve);
|
|
if (myClass == null)
|
|
myClass = cls;
|
|
return cls;
|
|
}
|
|
|
|
// if the class we want to load is this class loader's responsibility,
|
|
// load it, otherwise find/create a loader for the class
|
|
if (name.equals(myClassName))
|
|
{
|
|
if (myClass != null)
|
|
return myClass;
|
|
|
|
// load the class from it's file
|
|
Class c = findClass(name);
|
|
if (c != null && resolve == true)
|
|
resolveClass(c);
|
|
return c;
|
|
}
|
|
else
|
|
{
|
|
// see if we already have a loader for the class we want
|
|
script_class_loader newLoader = getClassLoader(name);
|
|
if (newLoader != null)
|
|
return newLoader.getMyClass();
|
|
}
|
|
System.err.println("WARNING: couldn't load class " + name + " in script_class_loader.loadClass");
|
|
return null;
|
|
} // loadClass(String, boolean)
|
|
|
|
/**
|
|
* Finds/creates a class.
|
|
*
|
|
* @param name class name
|
|
*
|
|
* @return the class
|
|
*/
|
|
protected Class findClass(String name) throws ClassNotFoundException
|
|
{
|
|
if (myClass == null)
|
|
{
|
|
try
|
|
{
|
|
byte data[] = loadClassData(name);
|
|
myClass = defineClass(name, data, 0, data.length);
|
|
|
|
// we need to keep track of all the superclasses of this class, in case one of them is reloaded
|
|
for (Class superClass = myClass.getSuperclass(); superClass != null; superClass = superClass.getSuperclass())
|
|
{
|
|
Object superLoader = loaderCache.get(superClass.getName());
|
|
if (superLoader != null)
|
|
((script_class_loader)superLoader).addDerivedClass(name);
|
|
}
|
|
}
|
|
catch (ClassFormatError err)
|
|
{
|
|
throw new ClassNotFoundException();
|
|
}
|
|
}
|
|
return myClass;
|
|
} // findClass
|
|
|
|
/**
|
|
* Loads a .class file from a file.
|
|
*
|
|
* @param name name of the class to load
|
|
*
|
|
* @return byte array containing the class data
|
|
*/
|
|
private byte[] loadClassData(String name) throws ClassNotFoundException
|
|
{
|
|
byte[] data = null;
|
|
|
|
// if the script name has '.' in it, convert them to '\'
|
|
String pathedName = name.replace('.', java.io.File.separatorChar);
|
|
String fullname = script_entry.getScriptPath() + pathedName + ".class";
|
|
|
|
String flag = base_class.getConfigSetting("GameServer", "scriptListLoadUnload");
|
|
|
|
if (!(flag == null || flag.length() < 1))
|
|
{
|
|
System.out.println("loadClassData " + fullname);
|
|
}
|
|
|
|
try
|
|
{
|
|
RandomAccessFile file = new RandomAccessFile(fullname, "r");
|
|
data = new byte[(int)file.length()];
|
|
file.read(data);
|
|
file.close();
|
|
}
|
|
catch (FileNotFoundException err)
|
|
{
|
|
System.err.println("WARNING: " + err.toString());
|
|
throw new ClassNotFoundException("file " + fullname + " not found");
|
|
}
|
|
catch (IOException err)
|
|
{
|
|
System.err.println("WARNING: " + err.toString());
|
|
throw new ClassNotFoundException("error reading file " + fullname);
|
|
}
|
|
|
|
return data;
|
|
} // loadClassData
|
|
|
|
/**
|
|
* Static data initialization.
|
|
*/
|
|
static
|
|
{
|
|
// initialize the NO_METHOD object
|
|
try
|
|
{
|
|
NO_METHOD = NO_OBJECT.getClass().getMethod("hashCode", (Class<?>[]) null);
|
|
}
|
|
catch( NoSuchMethodException err )
|
|
{
|
|
System.err.println("WARNING: " + err);
|
|
}
|
|
|
|
// initialize the defaultLoad set
|
|
defaultLoad = new TreeSet();
|
|
defaultLoad.add("script.attrib_mod");
|
|
defaultLoad.add("script.attribute");
|
|
defaultLoad.add("script.base_class");
|
|
defaultLoad.add("script.base_class$ammo_info");
|
|
defaultLoad.add("script.base_class$attacker_results");
|
|
defaultLoad.add("script.base_class$defender_results");
|
|
defaultLoad.add("script.base_class$range_info");
|
|
defaultLoad.add("script.collections");
|
|
defaultLoad.add("script.color");
|
|
defaultLoad.add("script.combat_engine");
|
|
defaultLoad.add("script.combat_engine$attack_roll_result");
|
|
defaultLoad.add("script.combat_engine$hit_result");
|
|
defaultLoad.add("script.combat_engine$combatant_data");
|
|
defaultLoad.add("script.combat_engine$attacker_data");
|
|
defaultLoad.add("script.combat_engine$defender_data");
|
|
defaultLoad.add("script.combat_engine$weapon_data");
|
|
defaultLoad.add("script.combat_engine$effect_data");
|
|
defaultLoad.add("script.combat_engine$combat_data");
|
|
defaultLoad.add("script.combat_engine$buff_data");
|
|
defaultLoad.add("script.custom_var");
|
|
defaultLoad.add("script.deltadictionary");
|
|
defaultLoad.add("script.dictionary");
|
|
defaultLoad.add("script.draft_schematic");
|
|
defaultLoad.add("script.draft_schematic$simple_ingredient");
|
|
defaultLoad.add("script.draft_schematic$slot");
|
|
defaultLoad.add("script.draft_schematic$attribute");
|
|
defaultLoad.add("script.draft_schematic$custom");
|
|
defaultLoad.add("script.location");
|
|
defaultLoad.add("script.map_location");
|
|
defaultLoad.add("script.menu_info");
|
|
defaultLoad.add("script.menu_info_data");
|
|
defaultLoad.add("script.menu_info_types");
|
|
defaultLoad.add("script.modifiable_float");
|
|
defaultLoad.add("script.modifiable_int");
|
|
defaultLoad.add("script.modifiable_string_id");
|
|
defaultLoad.add("script.obj_id");
|
|
defaultLoad.add("script.obj_id$pending_script");
|
|
defaultLoad.add("script.obj_id$obj_id_reference");
|
|
defaultLoad.add("script.obj_id$obj_id_cleanup");
|
|
defaultLoad.add("script.obj_id$emitter_tuple");
|
|
defaultLoad.add("script.obj_var");
|
|
defaultLoad.add("script.obj_var_list");
|
|
defaultLoad.add("script.palcolor_custom_var");
|
|
defaultLoad.add("script.player_levels");
|
|
defaultLoad.add("script.player_levels$level_data");
|
|
defaultLoad.add("script.player_levels$skill_template_data");
|
|
defaultLoad.add("script.prose_package");
|
|
defaultLoad.add("script.prose_package$participant_info");
|
|
defaultLoad.add("script.random");
|
|
defaultLoad.add("script.ranged_int_custom_var");
|
|
defaultLoad.add("script.region");
|
|
defaultLoad.add("script.resource_attribute");
|
|
defaultLoad.add("script.resource_density");
|
|
defaultLoad.add("script.resource_weight");
|
|
defaultLoad.add("script.resource_weight$weight");
|
|
defaultLoad.add("script.gcw_score");
|
|
defaultLoad.add("script.gcw_score$gcw_data");
|
|
defaultLoad.add("script.script_class_loader");
|
|
defaultLoad.add("script.script_entry");
|
|
defaultLoad.add("script.script_entry$listener_data");
|
|
defaultLoad.add("script.slot_data");
|
|
defaultLoad.add("script.string_id");
|
|
defaultLoad.add("script.transform");
|
|
defaultLoad.add("script.vector");
|
|
defaultLoad.add("script.system_process");
|
|
defaultLoad.add("script.terminal.terminal_character_builder$warp_location");
|
|
}
|
|
|
|
} // class script_class_loader
|
|
|