2012-03-30 10:11:13 -04:00
/*
2012-03-30 11:35:04 -04:00
* The FML Forge Mod Loader suite.
2012-03-30 10:11:13 -04:00
* Copyright (C) 2012 cpw
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package fml ;
2012-03-28 16:44:36 -04:00
import java.io.File ;
import java.io.FilenameFilter ;
import java.io.IOException ;
import java.net.MalformedURLException ;
2012-03-30 01:12:59 -04:00
import java.util.ArrayList ;
2012-03-28 16:44:36 -04:00
import java.util.Arrays ;
import java.util.Collections ;
2012-03-31 01:04:29 -04:00
import java.util.HashMap ;
2012-03-28 16:44:36 -04:00
import java.util.List ;
2012-03-30 01:12:59 -04:00
import java.util.Map ;
import java.util.logging.FileHandler ;
import java.util.logging.Level ;
2012-03-28 16:44:36 -04:00
import java.util.logging.Logger ;
2012-03-30 01:12:59 -04:00
import java.util.logging.SimpleFormatter ;
2012-03-28 16:44:36 -04:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipFile ;
2012-03-30 01:12:59 -04:00
import net.minecraft.src.BaseMod ;
2012-03-30 10:11:13 -04:00
import fml.ml.ModLoaderModContainer ;
2012-03-30 22:26:16 -04:00
import fml.obf.FMLHandler ;
2012-03-28 16:44:36 -04:00
2012-03-30 21:03:25 -04:00
public class Loader {
2012-03-30 01:12:59 -04:00
private enum State {
NOINIT , LOADING , PREINIT , INIT , POSTINIT , UP , ERRORED
};
2012-03-30 21:03:25 -04:00
private static Loader instance ;
2012-04-01 02:14:14 -04:00
public static Logger log = Logger . getLogger ( "ForgeModLoader" );
2012-03-28 16:44:36 -04:00
2012-03-30 01:12:59 -04:00
private static Pattern zipJar = Pattern . compile ( "([^\\s]+).(zip|jar)$" );
2012-03-31 00:59:43 -04:00
private static Pattern modClass = Pattern . compile ( "(.*/?)(mod\\_[^\\s]+).class$" );
2012-03-28 16:44:36 -04:00
2012-04-01 02:14:14 -04:00
private static String major = "@MAJOR@" ;
private static String minor = "@MINOR@" ;
private static String rev = "@REV@" ;
private static String build = "@BUILD@" ;
2012-03-30 21:03:25 -04:00
private static String mcversion = "@MCVERSION@" ;
private State state ;
private ModClassLoader modClassLoader ;
private List < ModContainer > mods ;
private Map < String , ModContainer > namedMods ;
public static Loader instance () {
2012-03-30 21:07:42 -04:00
if ( instance == null ) {
instance = new Loader ();
}
return instance ;
2012-03-30 21:03:25 -04:00
}
private Loader () {
2012-03-30 22:26:16 -04:00
Loader . log . setParent ( FMLHandler . getMinecraftLogger ());
2012-03-31 00:52:50 -04:00
Loader . log . setLevel ( Level . ALL );
2012-03-30 01:12:59 -04:00
FileHandler fileHandler ;
try {
fileHandler = new FileHandler ( "ForgeModLoader-%g.log" , 0 , 3 );
2012-03-31 00:43:54 -04:00
// We're stealing minecraft's log formatter
fileHandler . setFormatter ( FMLHandler . getMinecraftLogger (). getHandlers () [ 0 ] . getFormatter ());
2012-03-31 00:52:50 -04:00
fileHandler . setLevel ( Level . ALL );
2012-03-30 22:26:16 -04:00
Loader . log . addHandler ( fileHandler );
2012-03-30 01:12:59 -04:00
} catch ( Exception e ) {
// Whatever - give up
}
2012-04-01 02:14:14 -04:00
log . info ( String . format ( "Forge Mod Loader version %s.%s.%s.%s for Minecraft %s loading" , major , minor , rev , build , mcversion ));
2012-03-30 01:12:59 -04:00
}
private void sortModList () {
// NOOP for a minute
2012-03-28 16:44:36 -04:00
}
private void preModInit () {
2012-03-30 01:12:59 -04:00
state = State . PREINIT ;
2012-03-31 00:59:43 -04:00
log . fine ( "Beginning mod pre-initialization" );
2012-03-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
if ( mod . wantsPreInit ()) {
2012-03-31 00:59:43 -04:00
log . finer ( String . format ( "Pre-initializing %s" , mod . getSource ()));
2012-03-28 16:44:36 -04:00
mod . preInit ();
2012-03-30 01:12:59 -04:00
namedMods . put ( mod . getName (), mod );
2012-03-28 16:44:36 -04:00
}
}
2012-03-31 00:59:43 -04:00
log . fine ( "Mod pre-initialization complete" );
2012-03-28 16:44:36 -04:00
}
private void modInit () {
2012-03-30 01:12:59 -04:00
state = State . INIT ;
2012-03-31 00:59:43 -04:00
log . fine ( "Beginning mod initialization" );
2012-03-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
2012-03-31 00:59:43 -04:00
log . finer ( String . format ( "Initializing %s" , mod . getName ()));
2012-03-30 01:12:59 -04:00
mod . init ();
2012-03-28 16:44:36 -04:00
}
2012-03-31 00:59:43 -04:00
log . fine ( "Mod initialization complete" );
2012-03-28 16:44:36 -04:00
}
private void postModInit () {
2012-03-30 01:12:59 -04:00
state = State . POSTINIT ;
2012-03-31 00:59:43 -04:00
log . fine ( "Beginning mod post-initialization" );
2012-03-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
if ( mod . wantsPostInit ()) {
2012-03-31 00:59:43 -04:00
log . finer ( String . format ( "Post-initializing %s" , mod . getName ()));
2012-03-28 16:44:36 -04:00
mod . postInit ();
}
}
2012-03-31 00:59:43 -04:00
log . fine ( "Mod post-initialization complete" );
2012-03-28 16:44:36 -04:00
}
private void load () {
2012-03-31 02:28:14 -04:00
log . fine ( "Attempting to load mods contained in the minecraft jar file" );
attemptFileLoad ( new File ( "." , "minecraft_server.jar" ));
2012-03-28 16:44:36 -04:00
File modsDir = new File ( "." , "mods" );
String canonicalModsPath ;
try {
canonicalModsPath = modsDir . getCanonicalPath ();
} catch ( IOException ioe ) {
2012-03-31 00:59:43 -04:00
log . severe ( String . format ( "Failed to resolve mods directory mods %s" , modsDir . getAbsolutePath ()));
2012-03-30 22:26:16 -04:00
log . throwing ( "fml.server.Loader" , "initialize" , ioe );
2012-03-28 16:44:36 -04:00
throw new LoaderException ( ioe );
}
if ( ! modsDir . exists ()) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "No mod directory found, creating one: %s" , canonicalModsPath ));
2012-03-28 16:44:36 -04:00
try {
modsDir . mkdir ();
} catch ( Exception e ) {
2012-03-30 22:26:16 -04:00
log . throwing ( "fml.server.Loader" , "initialize" , e );
2012-03-28 16:44:36 -04:00
throw new LoaderException ( e );
}
}
if ( ! modsDir . isDirectory ()) {
2012-03-31 00:59:43 -04:00
log . severe ( String . format ( "Attempting to load mods from %s, which is not a directory" , canonicalModsPath ));
2012-03-28 16:44:36 -04:00
LoaderException loaderException = new LoaderException ();
2012-03-30 22:26:16 -04:00
log . throwing ( "fml.server.Loader" , "initialize" , loaderException );
2012-03-28 16:44:36 -04:00
throw loaderException ;
}
2012-03-31 00:59:43 -04:00
log . info ( String . format ( "Loading mods from %s" , canonicalModsPath ));
2012-03-28 16:44:36 -04:00
File [] modList = modsDir . listFiles ();
// Sort the files into alphabetical order first
Arrays . sort ( modList );
2012-03-30 01:12:59 -04:00
state = State . LOADING ;
2012-03-28 16:44:36 -04:00
for ( File modFile : modList ) {
if ( modFile . isDirectory ()) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "Found a directory %s, attempting to load it" , modFile . getName ()));
2012-03-31 00:43:54 -04:00
boolean modFound = attemptDirLoad ( modFile );
if ( modFound ) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "Directory %s loaded successfully" , modFile . getName ()));
2012-03-31 00:43:54 -04:00
} else {
2012-03-31 00:59:43 -04:00
log . info ( String . format ( "Directory %s contained no mods" , modFile . getName ()));
2012-03-31 00:43:54 -04:00
}
2012-03-28 16:44:36 -04:00
} else {
Matcher matcher = zipJar . matcher ( modFile . getName ());
2012-03-30 01:12:59 -04:00
if ( matcher . matches ()) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "Found a zip or jar file %s, attempting to load it" , matcher . group ( 0 )));
2012-03-31 00:43:54 -04:00
boolean modFound = attemptFileLoad ( modFile );
if ( modFound ) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "File %s loaded successfully" , matcher . group ( 0 )));
2012-03-31 00:43:54 -04:00
} else {
2012-03-31 00:59:43 -04:00
log . info ( String . format ( "File %s contained no mods" , matcher . group ( 0 )));
2012-03-31 00:43:54 -04:00
}
2012-03-28 16:44:36 -04:00
}
}
}
2012-03-30 01:12:59 -04:00
if ( state == State . ERRORED ) {
2012-03-31 00:59:43 -04:00
log . severe ( "A problem has occured during mod loading, giving up now" );
2012-03-28 16:44:36 -04:00
throw new RuntimeException ( "Giving up please" );
}
2012-03-31 00:59:43 -04:00
log . info ( String . format ( "Forge Mod Loader has loaded %d mods" , mods . size ()));
2012-03-28 16:44:36 -04:00
}
2012-03-31 00:43:54 -04:00
private boolean attemptDirLoad ( File modDir ) {
2012-03-28 16:44:36 -04:00
extendClassLoader ( modDir );
2012-03-30 01:12:59 -04:00
2012-03-31 00:43:54 -04:00
boolean foundAModClass = false ;
2012-03-30 01:12:59 -04:00
File [] content = modDir . listFiles ( new FilenameFilter () {
2012-03-28 16:44:36 -04:00
@Override
public boolean accept ( File dir , String name ) {
return modClass . matcher ( name ). find ();
}
});
for ( File modClassFile : content ) {
2012-03-30 01:12:59 -04:00
String clazzName = modClass . matcher ( modClassFile . getName ()). group ( 2 );
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "Found a mod class %s in directory %s, attempting to load it" , clazzName , modDir . getName ()));
2012-03-28 16:44:36 -04:00
loadModClass ( modDir , modClassFile . getName (), clazzName );
2012-03-30 22:26:16 -04:00
log . fine ( String . format ( "Successfully loaded mod class %s" , modClassFile . getName ()));
2012-03-31 00:43:54 -04:00
foundAModClass = true ;
2012-03-28 16:44:36 -04:00
}
2012-03-31 00:43:54 -04:00
return foundAModClass ;
2012-03-28 16:44:36 -04:00
}
private void loadModClass ( File classSource , String classFileName , String clazzName ) {
try {
2012-03-30 01:12:59 -04:00
Class <?> clazz = Class . forName ( clazzName , false , modClassLoader );
2012-03-28 16:44:36 -04:00
if ( clazz . isAnnotationPresent ( Mod . class )) {
// an FML mod
mods . add ( FMLModContainer . buildFor ( clazz ));
2012-03-30 01:12:59 -04:00
} else if ( BaseMod . class . isAssignableFrom ( clazz )) {
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "ModLoader BaseMod class %s found, loading" , clazzName ));
2012-03-30 01:12:59 -04:00
@SuppressWarnings ( "unchecked" )
Class <? extends BaseMod > bmClazz = ( Class <? extends BaseMod > ) clazz ;
2012-03-31 00:43:54 -04:00
ModContainer mc = new ModLoaderModContainer ( bmClazz , classSource . getCanonicalPath ());
2012-03-30 01:12:59 -04:00
mods . add ( mc );
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "ModLoader BaseMod class %s loaded" , clazzName ));
2012-03-28 16:44:36 -04:00
} else {
// Unrecognized
}
2012-03-30 01:12:59 -04:00
} catch ( Exception e ) {
2012-03-31 00:59:43 -04:00
log . warning ( String . format ( "Failed to load mod class %s in %s" , classFileName , classSource . getAbsoluteFile ()));
2012-03-30 22:26:16 -04:00
log . throwing ( "fml.server.Loader" , "attemptLoad" , e );
2012-03-30 01:12:59 -04:00
state = State . ERRORED ;
2012-03-28 16:44:36 -04:00
}
}
private void extendClassLoader ( File file ) {
2012-03-30 01:12:59 -04:00
if ( modClassLoader == null ) {
modClassLoader = new ModClassLoader ();
2012-03-28 16:44:36 -04:00
}
try {
modClassLoader . addFile ( file );
} catch ( MalformedURLException e ) {
throw new LoaderException ( e );
}
}
2012-03-31 00:43:54 -04:00
private boolean attemptFileLoad ( File modFile ) {
2012-03-28 16:44:36 -04:00
extendClassLoader ( modFile );
2012-03-30 01:12:59 -04:00
2012-03-31 00:43:54 -04:00
boolean foundAModClass = false ;
2012-03-28 16:44:36 -04:00
try {
2012-03-30 01:12:59 -04:00
ZipFile jar = new ZipFile ( modFile );
2012-03-28 16:44:36 -04:00
for ( ZipEntry ze : Collections . list ( jar . entries ())) {
2012-03-30 01:12:59 -04:00
Matcher match = modClass . matcher ( ze . getName ());
if ( match . matches ()) {
String pkg = match . group ( 1 ). replace ( '/' , '.' );
String clazzName = pkg + match . group ( 2 );
2012-03-31 00:59:43 -04:00
log . fine ( String . format ( "Found a mod class %s in file %s, attempting to load it" , clazzName , modFile . getName ()));
2012-03-28 16:44:36 -04:00
loadModClass ( modFile , ze . getName (), clazzName );
2012-03-30 22:26:16 -04:00
log . fine ( String . format ( "Mod class %s loaded successfully" , clazzName , modFile . getName ()));
2012-03-31 00:43:54 -04:00
foundAModClass = true ;
2012-03-28 16:44:36 -04:00
}
}
} catch ( Exception e ) {
2012-03-30 22:26:16 -04:00
log . warning ( String . format ( "Zip file %s failed to read properly" , modFile . getName ()));
log . throwing ( "fml.server.Loader" , "attemptFileLoad" , e );
2012-03-30 01:12:59 -04:00
state = State . ERRORED ;
2012-03-28 16:44:36 -04:00
}
2012-03-31 00:43:54 -04:00
return foundAModClass ;
2012-03-28 16:44:36 -04:00
}
2012-03-30 01:12:59 -04:00
public static List < ModContainer > getModList () {
2012-03-30 21:03:25 -04:00
return instance (). mods ;
2012-03-28 16:44:36 -04:00
}
2012-03-30 10:11:13 -04:00
public void loadMods () {
state = State . NOINIT ;
mods = new ArrayList < ModContainer > ();
2012-03-31 01:04:29 -04:00
namedMods = new HashMap < String , ModContainer > ();
2012-03-30 10:11:13 -04:00
load ();
sortModList ();
// Make mod list immutable
mods = Collections . unmodifiableList ( mods );
preModInit ();
}
public void initializeMods () {
modInit ();
postModInit ();
state = State . UP ;
2012-03-31 00:59:43 -04:00
log . info ( String . format ( "Forge Mod Loader load complete, %d mods loaded" , mods . size ()));
2012-03-30 22:26:16 -04:00
}
2012-03-31 00:43:54 -04:00
2012-03-30 22:26:16 -04:00
public static boolean isModLoaded ( String modname ) {
return instance (). namedMods . containsKey ( modname );
2012-03-30 10:11:13 -04:00
}
2012-03-28 16:44:36 -04:00
}