I am currently making a Minecraft Mod Loader.
package spideyzac;
import java.util.concurrent.CopyOnWriteArrayList;
import org.lwjgl.opengl.Display;
import spideyzac.Module;
public class Client {
public static String name = "Pizza Mod Loader", version = "1";
public static CopyOnWriteArrayList<Module> modules = new CopyOnWriteArrayList<Module>();
public static void startup() {
Display.setTitle(name + " v" + version);
}
public static void onKey(int key) {
for (Module m : modules) {
if (m.keyCode == key) {
m.toggle();
}
}
}
public static void update() {
for (Module m : modules) {
if (m.isEnabled()) {
m.ifEnabled();
} else {
m.ifNotEnabled();
}
}
}
}
As you can see above I have a class called Client. Startup is called when the Minecraft game launches. Now I have a folder named Mods and when startup is called I need to load the mods from the mods folder into the ArrayList named modules. More in depth, each Mod will be have a Main class which inherits this Module class
package spideyzac;
import org.lwjgl.input.Keyboard;
import net.minecraft.client.Minecraft;
public class Module {
public static int keyCode;
public static String name;
public static boolean enabled;
public Minecraft mc = Minecraft.getMinecraft();
Module (int keyCode, String name) {
this.keyCode = keyCode;
this.name = name;
}
public void onEnable() {
}
public void onDisable() {
}
public void toggle() {
enabled = !enabled;
if (enabled) {
onEnable();
} else {
onDisable();
}
}
public void ifEnabled() {
}
public void ifNotEnabled() {
}
public static boolean isEnabled() {
return enabled;
}
public static int getKeyCode() {
return keyCode;
}
public static boolean checkKey(int key) {
return Keyboard.isKeyDown(key);
}
}
So when startup is called, I need to go through each Mod in the mods folder and add a new instance of the module's Main class to the ArrayList modules.
There is a class named URLClassLoader for this.
As the name says, it can load classes from URLs:
File[] files=new File("Mods").listFiles();
URL[] urls=new URL[files.length];
for(int i=0;i<files.length;i++){
urls[i]=files[i].toURI().toURL();
}
URLClassLoader modLoader=new URLClassLoader(urls);
In one line:
URLClassLoader modLoader=new URLClassLoader(Stream.of(new File("Mods").list()).map(file->file.toURI().toURL()).toArray(URL[]::new));
You can also create one ClassLoader per JAR.
File[] files=new File("Mods").listFiles();
for(int i=0;i<files.length;i++){
URL url=files[i].toURI().toURL();
URLClassLoader modLoader=new URLClassLoader(url);
}
You can then load classes if you know the package name:
Class<?> cl=modLoader.loadClass("fully.qualified.name");
Object o=cl.getDeclaredConstructor().newInstance();
if(o instanceof Module){
modules.add((Module)o);
}
If you don't know the class names, you can use a library called Reflections in order to get the Class objects.
Related
I am currently making a Minecraft Mod Loader.
package spideyzac;
import java.util.concurrent.CopyOnWriteArrayList;
import org.lwjgl.opengl.Display;
import spideyzac.Module;
public class Client {
public static String name = "Pizza Mod Loader", version = "1";
public static CopyOnWriteArrayList<Module> modules = new CopyOnWriteArrayList<Module>();
public static void startup() {
Display.setTitle(name + " v" + version);
}
public static void onKey(int key) {
for (Module m : modules) {
if (m.keyCode == key) {
m.toggle();
}
}
}
public static void update() {
for (Module m : modules) {
if (m.isEnabled()) {
m.ifEnabled();
} else {
m.ifNotEnabled();
}
}
}
}
As you can see above I have a class called Client. Startup is called when the Minecraft game launches. Now I have a folder named Mods and when startup is called I need to load the mods from the mods folder into the ArrayList named modules. More in depth, each Mod will be have a Main class which inherits this Module class
package spideyzac;
import org.lwjgl.input.Keyboard;
import net.minecraft.client.Minecraft;
public class Module {
public static int keyCode;
public static String name;
public static boolean enabled;
public Minecraft mc = Minecraft.getMinecraft();
Module (int keyCode, String name) {
this.keyCode = keyCode;
this.name = name;
}
public void onEnable() {
}
public void onDisable() {
}
public void toggle() {
enabled = !enabled;
if (enabled) {
onEnable();
} else {
onDisable();
}
}
public void ifEnabled() {
}
public void ifNotEnabled() {
}
public static boolean isEnabled() {
return enabled;
}
public static int getKeyCode() {
return keyCode;
}
public static boolean checkKey(int key) {
return Keyboard.isKeyDown(key);
}
}
So when startup is called, I need to go through each Mod in the mods folder and add a new instance of the module's Main class to the ArrayList modules.
There is a class named URLClassLoader for this.
As the name says, it can load classes from URLs:
File[] files=new File("Mods").listFiles();
URL[] urls=new URL[files.length];
for(int i=0;i<files.length;i++){
urls[i]=files[i].toURI().toURL();
}
URLClassLoader modLoader=new URLClassLoader(urls);
In one line:
URLClassLoader modLoader=new URLClassLoader(Stream.of(new File("Mods").list()).map(file->file.toURI().toURL()).toArray(URL[]::new));
You can also create one ClassLoader per JAR.
File[] files=new File("Mods").listFiles();
for(int i=0;i<files.length;i++){
URL url=files[i].toURI().toURL();
URLClassLoader modLoader=new URLClassLoader(url);
}
You can then load classes if you know the package name:
Class<?> cl=modLoader.loadClass("fully.qualified.name");
Object o=cl.getDeclaredConstructor().newInstance();
if(o instanceof Module){
modules.add((Module)o);
}
If you don't know the class names, you can use a library called Reflections in order to get the Class objects.
I wonder what the requirements are so that the JTree properly works. I have written some code, displaying a filesystem tree and then call expandPath() to expand a path on it.
When I use a File object everything works fine, but the tree shows the full path on each node. So I wrapped it in a class which changes the toString() so that only the dir/filename is shown, but now expandPath() no longer works and the path is not expanded.
package tools.controls.TreeControl.Filesystem;
import java.io.File;
import java.net.URI;
public class TreeFile
extends File
implements Cloneable
, Comparable<File>
{
private static final long serialVersionUID = 1L;
public TreeFile(TreeFile oOther)
{
super(oOther.getParentFile(), oOther.getName());
}
public TreeFile(File oFile)
{
super(oFile.getParentFile(), oFile.getName());
}
public TreeFile(String oPath)
{
super(oPath);
}
public TreeFile(URI oFileURL)
{
super(oFileURL);
}
public TreeFile(String oParent, String oChild)
{
super(oParent, oChild);
}
public TreeFile(File oParent, String oChild)
{
super(oParent, oChild);
}
public TreeFile getParentTreeFile()
{
File f = getParentFile();
if(f == null)
return null;
return new TreeFile(f);
}
public String toString()
{
return getName();
}
public TreeFile clone()
{
return new TreeFile(this);
}
#Override
public int compareTo(File oObject)
{
return super.compareTo(oObject);
}
}
I am programming a plugin system for my Java Application. My problem is if I tryto cast from the Child Class to my Parent Interface
I get the ClassCastException java.lang.ClassCastException: net.scrumplex.testplugin.Main cannot be cast to net.scrumplex.sprummlbot.plugins.SprummlPlugin
My Interface:
package net.scrumplex.sprummlbot.plugins;
import com.github.theholywaffle.teamspeak3.api.event.BaseEvent;
import com.github.theholywaffle.teamspeak3.api.event.TS3EventType;
public interface SprummlPlugin {
public boolean init(String version);
public void end();
public void handleEvent(TS3EventType type, BaseEvent event);
}
My TestPlugin:
package net.scrumplex.testplugin;
import com.github.theholywaffle.teamspeak3.api.event.BaseEvent;
import com.github.theholywaffle.teamspeak3.api.event.TS3EventType;
import net.scrumplex.sprummlbot.plugins.SprummlPlugin;
public class Main implements SprummlPlugin {
#Override
public void end() {
System.out.println("Stopped");
}
#Override
public void handleEvent(TS3EventType type, BaseEvent event) {
System.out.println(type.name());
}
#Override
public boolean init(String arg0) {
System.out.println("Test");
return true;
}
}
My PluginLoader:
public static void load(File jarFile) throws InstantiationException, IllegalAccessException, ClassNotFoundException,
IOException, PluginLoadException {
String path = "";
JarFile jar = new JarFile(jarFile);
JarEntry entry = jar.getJarEntry("plugin.ini");
InputStream input = jar.getInputStream(entry);
Ini ini = new Ini(input);
if (!ini.containsKey("Plugin")) {
jar.close();
throw new PluginLoadException("Ini file not compatible.");
}
Section sec = ini.get("Plugin");
if (sec.containsKey("main")) {
path = sec.get("main");
}
ClassLoader loader = URLClassLoader.newInstance(new URL[] { jarFile.toURI().toURL() });
SprummlPlugin plugin = (SprummlPlugin) loader.loadClass(path).newInstance();
pluginslist.add(plugin);
plugin.init(Vars.VERSION);
jar.close();
}
The class path for the TestPlugin is correct
How I fixed this:
I added this.getClass().getClassLoader()to my URLClassLoader.newInstance(...);
So I'm working on a plugin loader for a game and what it does is load plugins (class objects) from a .jar inside a folder, then add them to an ArrayList (so users can make their own mods). But my java experience isn't the best :/ So far it gets the classes in the jar but I need to check what methods are in the class (such as the constructor for labeling). Or if someone knows a better way to do this. Any help would be appreciated.
Plugin.java:
package me.rigamortis.faurax.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import net.minecraft.client.Minecraft;
import me.rigamortis.faurax.core.Core;
public class Plugin {
public Minecraft mc = Minecraft.getMinecraft();
final File folder = new File(mc.mcDataDir + File.separator + "Faurax/Plugins");
public String jar;
public static String className;
private String name;
private String desc;
private int color;
private int key;
public Plugin() {
if(!folder.exists())
folder.mkdirs();
loadPlugins(folder);
if(folder != null)
getClassNames(folder);
}
public void setName(String newName) {
name = newName;
}
public void setDesc(String newDesc) {
desc = newDesc;
}
public void setColor(int newColor) {
color = newColor;
}
public void setKey(int newKey) {
key = newKey;
}
public String getName() {
return this.name;
}
public String getDesc() {
return this.desc;
}
public int getKey() {
return this.key;
}
public int getColor() {
return this.color;
}
/**
* A hook for plugins
*/
public void onTick() {}
public void loadPlugins(final File folder) {
try{
for (final File fileEntry : folder.listFiles()) {
if ( !fileEntry.isDirectory()) {
if(fileEntry.getName().endsWith(".jar")){
jar = fileEntry.getName();
Core.logNormal("Loading " + fileEntry.getName());
}
}
}
}
catch(Exception e) {
Core.logError("Error? jar isin't a plugin.");
e.printStackTrace();
}
}
public void getClassNames(final File dir) {
try{
ZipInputStream zip = new ZipInputStream(new FileInputStream(dir + File.seperator + jar));
for(ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry())
if(entry.getName().endsWith(".class") && !entry.isDirectory()) {
Core.logNormal("Found "+entry);
className = entry.toString();
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
PluginLoader.java:
package me.rigamortis.faurax.plugin;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import me.rigamortis.faurax.core.Core;
public class PluginLoader {
public static List<Plugin> plugins = new ArrayList<Plugin>();
public PluginLoader() {
loadPlugins();
}
public void loadPlugins() {}
}
I answered a similar question to this not too long ago. Here's the code to get all methods from a given class, I think it's fairly self-explanatory:
void foo(Object o)
{
Class<?> c = o.getClass();
for (Method m : c.getDeclaredMethods())
{
//do whatever
}
}
This takes advantage of the Java's Reflection API. You can read more about it here.
Can i run a method from am Plugin like onEnable(), my script doesnt work. :(
The extended Class:
package de.R3N3PDE.X.API;
import de.R3N3PDE;
public class JavaPlugin{
Server server;
public ServerManager getServer(){
return new ServerManager(server);
}
public ServerManager setServer(Server s){
server = s;
}
public PluginLogger getLogger(){
return new PluginLogger(server);
}
}
The Plugin:
package de.R3N3PDE.PluginExample;
import de.R3N3PDE.X.API.JavaPlugin;
public class PluginExample extends JavaPlugin{
public void onEnable(){
getLogger().info("Plugin Enabled!");
}
}
The PluginLoader:
package de.R3N3PDE.X.Plugin;
imports de.R3N3PDE.X.API.JavaPlugin;
public void initPlugins(){
try {
File loc = new File("plugins");
extendClasspath(loc);
ServiceLoader<JavaPlugin> sl = ServiceLoader.load(JavaPlugin.class);
Iterator<JavaPlugin> apit = sl.iterator();
while (apit.hasNext()){;
JavaPlugin met = apit.next();
//Set the Server
med.setServer(s);
//run void onEnable(); in the Plugin????
}
} catch (IOException e) {
e.printStackTrace();
}
}
====================
EDIT:
Thanks. I have a new Problem...
The URLClassLoader find Plugins but the ServiceLoader can´t load the Plugin.
I think the ServiceLoader cant fint the extended class.
The Plugin:
package de.R3N3PDE.PluginExampleName;
import de.CodingDev.X.Server.API.JavaPlugin;
public class PluginExampleName extends JavaPlugin{
#Override
public void onEnable() {
System.out.println("Test");
getLogger().info("Plugin Enabled!");
}
}
I have make a new Plugin Loader the old version dosen´t work:
public void onLoad(){
File loc = new File("plugins");
File[] flist = loc.listFiles(new FileFilter() {
public boolean accept(File file) {return file.getPath().toLowerCase().endsWith(".jar");}
});
URL[] urls = new URL[flist.length];
for (int i = 0; i < flist.length; i++)
urls[i] = flist[i].toURI().toURL();
URLClassLoader ucl = new URLClassLoader(urls);
for(URL url : ucl.getURLs()){
s.getLogger().info("PluginLoader", "Found Plugin: " + url.getFile());
}
ServiceLoader<JavaPlugin> sl = ServiceLoader.load(JavaPlugin.class, ucl);
Iterator<JavaPlugin> apit = sl.iterator();
while (apit.hasNext()){
s.getLogger().info("PluginLoader", "Loading Plugin");
apit.next().onEnable();
}
}
The Log:
[LOG] [02.11.2013 15:26:09] [PluginLoader] [INFO] Loading Plugins...
[LOG] [02.11.2013 15:26:09] [PluginLoader] [INFO] Found Plugin: /C:/Users/René/Desktop/Server/plugins/CommandLogger.jar
What about making your class abstract with abstract onEnable method?
public abstract class JavaPlugin{
Server server;
public ServerManager getServer(){
return new ServerManager(server);
}
public ServerManager setServer(Server s){
server = s;
}
public PluginLogger getLogger(){
return new PluginLogger(server);
}
public abstract void onEnable();
}
And then you have to implement onEnable method in your subclasses.
You could then call the onEnable method of any dynamically generated subclass that extends the JavaPlugin.
The second way could be implementing the onEnable method in Javaplugin and then overriding the method in an extended class.
public class JavaPlugin {
...
public void onEnable(){
getLogger().info("JavaPlugin onEnable default implementation!");
}
}
In your PluginExample file:
public class PluginExample extends JavaPlugin{
#Override
public void onEnable(){
getLogger().info("Plugin Enabled!");
}
}