Thoughts on how to do the modloading properly
This commit is contained in:
parent
0cc2bc1923
commit
62cca5a4b7
|
@ -0,0 +1,15 @@
|
||||||
|
package fml;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Mod {
|
||||||
|
String name() default "";
|
||||||
|
String version() default "";
|
||||||
|
boolean wantsPreInit() default false;
|
||||||
|
boolean wantsPostInit() default false;
|
||||||
|
public @interface PreInit {}
|
||||||
|
public @interface Init {}
|
||||||
|
public @interface PostInit {}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package fml.server;
|
||||||
|
|
||||||
|
import fml.Mod;
|
||||||
|
|
||||||
|
public class FMLModContainer implements ModContainer {
|
||||||
|
private Mod modDescriptor;
|
||||||
|
private Object modInstance;
|
||||||
|
public FMLModContainer(Class<?> clazz) {
|
||||||
|
modDescriptor=clazz.getAnnotation(Mod.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
modInstance=clazz.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wantsPreInit() {
|
||||||
|
return modDescriptor.wantsPreInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wantsPostInit() {
|
||||||
|
return modDescriptor.wantsPostInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModContainer buildFor(Class<?> clazz) {
|
||||||
|
return new FMLModContainer(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
package fml.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import fml.Mod;
|
||||||
|
import fml.stubs.mcpserver.BaseMod;
|
||||||
|
|
||||||
|
public class Loader {
|
||||||
|
private enum State { NOINIT, LOADING, PREINIT, INIT, POSTINIT, UP, ERRORED };
|
||||||
|
private static State state;
|
||||||
|
private static Logger LOG = Logger.getLogger("ForgeModLoader.Loader");
|
||||||
|
|
||||||
|
private static List<ModContainer> mods;
|
||||||
|
|
||||||
|
private static ModClassLoader modClassLoader;
|
||||||
|
|
||||||
|
private static Pattern zipJar = Pattern.compile("([\\w]+).(zip|jar)$");
|
||||||
|
private static Pattern modClass = Pattern.compile("(.*)(mod_[^\\s]+)\\.class$");
|
||||||
|
|
||||||
|
public static Loader instance;
|
||||||
|
|
||||||
|
public static void run() {
|
||||||
|
instance=new Loader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Loader() {
|
||||||
|
state=State.NOINIT;
|
||||||
|
load();
|
||||||
|
preModInit();
|
||||||
|
modInit();
|
||||||
|
postModInit();
|
||||||
|
state=State.UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preModInit() {
|
||||||
|
state=State.PREINIT;
|
||||||
|
for (ModContainer mod : mods) {
|
||||||
|
if (mod.wantsPreInit()) {
|
||||||
|
mod.preInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void modInit() {
|
||||||
|
state=State.INIT;
|
||||||
|
for (ModContainer mod : mods) {
|
||||||
|
mod.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postModInit() {
|
||||||
|
state=State.POSTINIT;
|
||||||
|
for (ModContainer mod : mods) {
|
||||||
|
if (mod.wantsPostInit()) {
|
||||||
|
mod.postInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load() {
|
||||||
|
File modsDir = new File(".", "mods");
|
||||||
|
String canonicalModsPath;
|
||||||
|
try {
|
||||||
|
canonicalModsPath = modsDir.getCanonicalPath();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
LOG.severe(String.format("Failed to resolve mods directory mods %s",modsDir.getAbsolutePath()));
|
||||||
|
LOG.throwing("fml.server.Loader", "initialize", ioe);
|
||||||
|
throw new LoaderException(ioe);
|
||||||
|
}
|
||||||
|
if (!modsDir.exists()) {
|
||||||
|
LOG.fine(String.format("No mod directory found, creating one: %s", canonicalModsPath));
|
||||||
|
try {
|
||||||
|
modsDir.mkdir();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.throwing("fml.server.Loader", "initialize", e);
|
||||||
|
throw new LoaderException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!modsDir.isDirectory()) {
|
||||||
|
LOG.severe(String.format("Attempting to load mods from %s, which is not a directory", canonicalModsPath));
|
||||||
|
LoaderException loaderException = new LoaderException();
|
||||||
|
LOG.throwing("fml.server.Loader", "initialize", loaderException);
|
||||||
|
throw loaderException;
|
||||||
|
}
|
||||||
|
File[] modList = modsDir.listFiles();
|
||||||
|
// Sort the files into alphabetical order first
|
||||||
|
Arrays.sort(modList);
|
||||||
|
|
||||||
|
state=State.LOADING;
|
||||||
|
for (File modFile : modList) {
|
||||||
|
if (modFile.isDirectory()) {
|
||||||
|
LOG.info(String.format("Found directory %s. Attempting load", modFile.getName()));
|
||||||
|
attemptDirLoad(modFile);
|
||||||
|
} else {
|
||||||
|
Matcher matcher = zipJar.matcher(modFile.getName());
|
||||||
|
if (matcher.find()) {
|
||||||
|
LOG.info(String.format("Found zip or jar file %s. Attempting load.", matcher.group(0)));
|
||||||
|
attemptFileLoad(modFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state==State.ERRORED) {
|
||||||
|
LOG.severe("A problem has occured during mod loading. Giving up now");
|
||||||
|
throw new RuntimeException("Giving up please");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attemptDirLoad(File modDir) {
|
||||||
|
extendClassLoader(modDir);
|
||||||
|
|
||||||
|
File[] content=modDir.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return modClass.matcher(name).find();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (File modClassFile : content) {
|
||||||
|
LOG.fine(String.format("Found a mod class %s in directory %s. Attempting to load it",modClassFile.getName(),modDir.getName()));
|
||||||
|
String clazzName=modClass.matcher(modClassFile.getName()).group(2);
|
||||||
|
loadModClass(modDir, modClassFile.getName(), clazzName);
|
||||||
|
LOG.fine(String.format("Successfully loaded mod class %s",modClassFile.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadModClass(File classSource, String classFileName, String clazzName) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz=Class.forName(clazzName,true,modClassLoader);
|
||||||
|
if (clazz.isAnnotationPresent(Mod.class)) {
|
||||||
|
// an FML mod
|
||||||
|
mods.add(FMLModContainer.buildFor(clazz));
|
||||||
|
} else if (clazz.isAssignableFrom(BaseMod.class)) {
|
||||||
|
// a modloader mod
|
||||||
|
} else {
|
||||||
|
// Unrecognized
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LOG.warning(String.format("Failed to load mod class %s in %s",classFileName,classSource.getName()));
|
||||||
|
LOG.throwing("fml.server.Loader", "attemptDirLoad", e);
|
||||||
|
state=State.ERRORED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extendClassLoader(File file) {
|
||||||
|
if (modClassLoader==null) {
|
||||||
|
modClassLoader=new ModClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
modClassLoader.addFile(file);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new LoaderException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attemptFileLoad(File modFile) {
|
||||||
|
extendClassLoader(modFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ZipFile jar=new ZipFile(modFile);
|
||||||
|
for (ZipEntry ze : Collections.list(jar.entries())) {
|
||||||
|
Matcher match=modClass.matcher(ze.getName());
|
||||||
|
if (match.find()) {
|
||||||
|
String pkg=match.group(1).replace('/', '.');
|
||||||
|
String clazzName=pkg+match.group(2);
|
||||||
|
loadModClass(modFile, ze.getName(), clazzName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warning(String.format("Zip file %s failed to read properly", modFile.getName()));
|
||||||
|
LOG.throwing("fml.server.Loader", "attemptFileLoad", e);
|
||||||
|
state=State.ERRORED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoaderException extends RuntimeException {
|
||||||
|
public LoaderException(Exception wrapped) {
|
||||||
|
super(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoaderException() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package fml.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
public class ModClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
public ModClassLoader() {
|
||||||
|
super(new URL[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFile(File modFile) throws MalformedURLException {
|
||||||
|
URL url=modFile.toURI().toURL();
|
||||||
|
super.addURL(url);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package fml.server;
|
||||||
|
|
||||||
|
public interface ModContainer {
|
||||||
|
boolean wantsPreInit();
|
||||||
|
boolean wantsPostInit();
|
||||||
|
void preInit();
|
||||||
|
void init();
|
||||||
|
void postInit();
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package fml.server;
|
||||||
|
|
||||||
|
public class ModLoaderModContainer implements ModContainer {
|
||||||
|
public boolean wantsPreInit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wantsPostInit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preInit() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,12 +2,16 @@ package fml.stubs.mcpserver;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import fml.Mod;
|
||||||
|
|
||||||
|
|
||||||
import net.minecraft.src.EntityPlayer;
|
import net.minecraft.src.EntityPlayer;
|
||||||
import net.minecraft.src.IInventory;
|
import net.minecraft.src.IInventory;
|
||||||
import net.minecraft.src.ItemStack;
|
import net.minecraft.src.ItemStack;
|
||||||
import net.minecraft.src.Packet250CustomPayload;
|
import net.minecraft.src.Packet250CustomPayload;
|
||||||
import net.minecraft.src.World;
|
import net.minecraft.src.World;
|
||||||
|
|
||||||
|
@Mod(name="blah",version="blah")
|
||||||
public interface BaseMod {
|
public interface BaseMod {
|
||||||
int addFuel(int id, int metadata);
|
int addFuel(int id, int metadata);
|
||||||
|
|
||||||
|
@ -25,8 +29,10 @@ public interface BaseMod {
|
||||||
abstract String getVersion();
|
abstract String getVersion();
|
||||||
|
|
||||||
// void keyboardEvent(KeyBinding event);
|
// void keyboardEvent(KeyBinding event);
|
||||||
|
@Mod.PreInit
|
||||||
abstract void load();
|
abstract void load();
|
||||||
|
|
||||||
|
@Mod.Init
|
||||||
void modsLoaded();
|
void modsLoaded();
|
||||||
|
|
||||||
void onItemPickup(EntityPlayer player, ItemStack item);
|
void onItemPickup(EntityPlayer player, ItemStack item);
|
||||||
|
|
Loading…
Reference in New Issue