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.
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 am trying to develop a dynamic Factory Class, where Factory Class does not know the factories, I will put the code so that the question is clearer.
App.java:
package br.com.factory;
public class App {
public static void main(String[] args) {
Product product = ProductFactory.createProduct("Xiaomi", "MI XX");
if (product != null) {
System.out.println(product.getName());
}
}
}
Product.java:
package br.com.factory;
public interface Product {
public String getName();
}
ProductFactory.java:
package br.com.factory;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ProductFactory {
private static HashMap<String, Map<String, Supplier<Product>>> registries = new HashMap<>();
private ProductFactory(){}
static {
ClassLoaderInitializer.initialize(ProductSupplier.class);
}
public static Product createProduct(String manufacturer, String model) {
Map<String, Supplier<Product>> manufacturers = registries.getOrDefault(manufacturer, null);
if (manufacturers != null){
Supplier<Product> suppliers = manufacturers.getOrDefault(model, null);
if (suppliers != null) {
return suppliers.get();
}
}
return null;
}
public static void registerFactory(String manufacturer, String model, Supplier<Product> supplier) {
registries
.computeIfAbsent(manufacturer, p -> new HashMap<>())
.putIfAbsent(model, supplier);
}
}
ProductSupplier.java:
package br.com.factory;
import java.util.function.Supplier;
public interface ProductSupplier extends Supplier<Product> {
}
XiaomiFactory.java:
package br.com.factory.xiaomi;
import br.com.factory.Product;
import br.com.factory.ProductFactory;
import br.com.factory.ProductSupplier;
public class XiaomiFactory implements ProductSupplier {
static {
ProductFactory.registerFactory("Xiaomi", "MI XX", XiamoMiXX::new);
}
private XiaomiFactory() {
}
#Override
public Product get() {
return new XiamoMiXX();
}
}
class XiamoMiXX implements Product {
#Override
public String getName() {
return "Xiaomi Mi XX";
}
}
ClassLoaderInitializer.java:
package br.com.factory;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class ClassLoaderInitializer {
private ClassLoaderInitializer() {
}
public static void initialize(Class<?> parentClass) {
try {
Enumeration<URL> resources = ClassLoaderInitializer.class.getClassLoader().getResources("");
while (resources.hasMoreElements()) {
URL nextElement = resources.nextElement();
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { nextElement });) {
URL[] urLs = urlClassLoader.getURLs();
for (URL u : urLs) {
try {
File file = new File(u.toURI());
initializeClass(file, file, urlClassLoader, parentClass);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void initializeClass(File root, File file, ClassLoader loader, Class<?> parentClass) {
if (file.isDirectory()) {
File[] listFiles = file.listFiles();
for (File f : listFiles) {
initializeClass(root, f, loader, parentClass);
}
} else {
if (file.getName().toUpperCase().endsWith(".class".toUpperCase())) {
try {
String fileName = file.toString();
String className = fileName.substring(root.toString().length() + 1,
fileName.toUpperCase().lastIndexOf(".class".toUpperCase())).replace(File.separator, ".");
Class<?> clazz = Class.forName(className, false, loader);
if (clazz.isAssignableFrom(parentClass)) {
Class.forName(className, true, loader);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
The problem occurs in the initializeClass method,
more precisely in:
Class <?> Clazz = Class.forName (className, false, loader);
if (clazz.isAssignableFrom (parentClass)) {
Class.forName (className, true, loader);
}
the idea of the ClassLoaderInitializer class is to initialize the classes that inherit from a given class "Class <?> parentClass"
but when I call the method
Class.forName (className, true, loader);
for the second time, passing true in the second parameter, the class is not initialized.
if I call:
Class.forName (className, true, loader);
directly, the initializeClass method will initialize all classes, what I would not like to happen.
is there any outside of me explicitly initializing (forcing) the class specifies?
Use ServiceLoader
ServiceLoader allows you to register services using metadata in the META-INF/services/ folder. It allows you to use a Java API to register all your services at once. It's better than trying to do it yourself, especially since it's standardized and doesn't require "magic" to get registered. Magic which might be broken in Java 9 and later with the introduction of modules.
This is how you should use it:
ProductSupplier.java
public interface ProductSupplier extends Supplier<Product> {
// Add these methods
String getManufacturer();
String getModel();
}
ProductFactory.java
public class ProductFactory {
private static final Map<List<String>, ProductSupplier> SUPPLIERS;
static {
var suppliers = new HashMap<List<String>, ProductSupplier>();
for (var supplier : ServiceLoader.load(ProductSupplier.class)) { // Yes, it's as easy as this.
var key = List.of(supplier.getManufacturer(), supplier.getModel());
suppliers.put(key, supplier);
}
SUPPLIERS = Map.copyOf(suppliers);
}
public static Product createProduct(String manufacturer, String model) {
var key = List.of(manufacturer, model);
var supplier = suppliers.getOrDefault(key, () -> null);
return supplier.get();
}
}
XiaomiFactory.java
public class XiaomiFactory implements ProductSupplier {
#Override public String getManufacturer() { return "Xiaomi"; }
#Override public String getModel() { return "Mi XX"; }
#Override public Product get() { return new XiaomiMiXX(); }
}
In META-INF/services/com.br.factory.ProductSupplier:
com.br.factory.xiaomi.XiaomiFactory
com.br.factory.samsung.SamsungFactory # Need to create
com.br.factory.apple.AppleFactory # Need to create
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 trying to save obj in files but java.io.NotSerializableException error doesnt allow me to do it.
This is my code:
import Estrategia.Gestor_nivel;
import Resources.Exceptions.DuplicateLevelsId;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import pp_epoca_normal.Gerir_jogo;
public class Teste {
public void guardar_ficheiro(Object obt) {
Gerir_jogo teste = (Gerir_jogo) obt;
System.out.println("sad---- " + teste);
Gestor_nivel sad;
try {
ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream("teste.dat"));
ObjectOutputStream objOut1 = new ObjectOutputStream(new FileOutputStream("teste1.dat"));
objOut.writeObject(teste);
objOut1.writeObject(teste.getObjetos());
sad = (Gestor_nivel) teste.getLevel(0);
objOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void ler_ficheiro() throws FileNotFoundException, IOException, ClassNotFoundException, DuplicateLevelsId {
Gerir_jogo asd;
ObjectInputStream mySecondStream = new ObjectInputStream(new FileInputStream("teste.dat"));
ObjectInputStream mySecondStream1 = new ObjectInputStream(new FileInputStream("teste1.dat"));
asd = (Gerir_jogo) mySecondStream.readObject();
asd.setObjetos((Object[]) mySecondStream1.readObject());
System.out.println("leu--");
mySecondStream.close();
mySecondStream1.close();
}
}
Class gerir jogo
import Container.Contentor;
import Estrategia.Gestor_nivel;
import Resources.Exceptions.DuplicateLevelsId;
import Resources.GameContainerContract;
import Resources.GameLevelContract;
public class Gerir_jogo extends Contentor implements GameContainerContract {
private String nome_jogo;
private boolean mode_jogo_depuracao;
public Gerir_jogo(String nome_jogo, boolean mode_jogo) {
this.nome_jogo = nome_jogo;
this.mode_jogo_depuracao = mode_jogo;
}
#Override
public boolean addNewLevel(GameLevelContract glc) throws DuplicateLevelsId {
Gestor_nivel a;
boolean asd = false;
for (Object objetos : this.getObjetos()) {
a = (Gestor_nivel) objetos;
if(objetos != null)
if (a.getId() == glc.getId()) {
asd = true;
}
}
if (asd == false)
return super.addObject(glc);
return false;
}
#Override
public boolean removeLevel(GameLevelContract glc) {
return super.Encontrar_objeto(super.removeObject(super.findObject(glc)));
}
#Override
public GameLevelContract getLevel(int i) {
return (GameLevelContract) super.getObject(i);
}
#Override
public int getSize() {
return super.getCont();
}
#Override
public boolean getDebugMode() {
return this.mode_jogo_depuracao;
}
#Override
public void setDebugMode(boolean bln) {
this.mode_jogo_depuracao = bln;
}
#Override
public void setName(String string) {
this.nome_jogo = string;
}
#Override
public String getName() {
return this.nome_jogo;
}
Can someone help me please?
I really need to put this saving and reading files and what is inside them
Error:
java.io.NotSerializableException: pp_epoca_normal.Gerir_jogo
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Gravar_ler_ficheiro.Teste.guardar_ficheiro(Teste.java:34)
at pp_epoca_normal.PP_epoca_normal.main(PP_epoca_normal.java:77)
I really dont know what to do
Your class Gerir_jogo must implements the interface Serializable. Please, take a look at this tutorial.
Edit 1
Your class does not implements it, so, change if for:
import java.io.Serializable;
public class Gerir_jogo extends Contentor implements GameContainerContract, Serializable {
Implement Serializable on Gerir_jogo class.
Change class declaration as:
import java.io.Serializable;
public class Gerir_jogo extends Contentor implements GameContainerContract, Serializable {
To serialize java object means to stream the byte information as non-static states of the object to external file. Here the class must be implemented by the interface "Serilizable".
Here I have replied with a code, which copies the States as id, name & sal of the class "Employee" to an external file present at the destination "D:\Object\States.txt". Here its not readable.
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Employee implements Serializable {
int id;
String name;
double sal;
public Employee(int id, String name, double sal) {
super();
this.id = id;
this.name = name;
this.sal = sal;
}
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", sal=" + sal + "]";
}
public static void main(String[] args) throws Exception {
Employee e1 = new Employee(101, "Mr. S.V. Roshan", 45000.0D);
FileOutputStream fout = new FileOutputStream("D:/Object/State.txt");
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(e1);
out.flush();
out.close();
fout.flush();
fout.close();
System.out.println("Object serialized successfully!");
}
}
I think, It may help you..!