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 ;
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-28 16:44:36 -04:00
2012-03-30 10:11:13 -04:00
public enum Loader {
INSTANCE ;
2012-03-30 01:12:59 -04:00
private enum State {
NOINIT , LOADING , PREINIT , INIT , POSTINIT , UP , ERRORED
};
2012-03-28 16:44:36 -04:00
private static State state ;
private static Logger LOG = Logger . getLogger ( "ForgeModLoader.Loader" );
private static List < ModContainer > mods ;
2012-03-30 01:12:59 -04:00
private static Map < String , ModContainer > namedMods ;
2012-03-28 16:44:36 -04:00
private static ModClassLoader modClassLoader ;
2012-03-30 01:12:59 -04:00
private static Pattern zipJar = Pattern . compile ( "([^\\s]+).(zip|jar)$" );
private static Pattern modClass = Pattern . compile ( "(.*/)(mod\\_[^\\s]+).class$" );
2012-03-28 16:44:36 -04:00
public static void run () {
2012-03-30 01:12:59 -04:00
LOG . setLevel ( Level . ALL );
FileHandler fileHandler ;
try {
fileHandler = new FileHandler ( "ForgeModLoader-%g.log" , 0 , 3 );
System . setProperty ( "java.util.logging.SimpleFormatter.format" , "%1$tc %2$s%n%4$s: %5$s%6$s%n" );
fileHandler . setFormatter ( new SimpleFormatter ());
fileHandler . setLevel ( Level . ALL );
LOG . addHandler ( fileHandler );
} catch ( Exception e ) {
// Whatever - give up
}
2012-03-28 16:44:36 -04:00
}
2012-03-30 01:12:59 -04:00
2012-03-28 16:44:36 -04:00
private Loader () {
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-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
if ( mod . wantsPreInit ()) {
2012-03-30 01:12:59 -04:00
LOG . finer ( String . format ( "Pre-initializing %s" , mod . getName ()));
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
}
}
}
private void modInit () {
2012-03-30 01:12:59 -04:00
state = State . INIT ;
2012-03-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
2012-03-30 01:12:59 -04:00
LOG . finer ( String . format ( "Initializing %s" , mod . getName ()));
mod . init ();
2012-03-28 16:44:36 -04:00
}
}
private void postModInit () {
2012-03-30 01:12:59 -04:00
state = State . POSTINIT ;
2012-03-28 16:44:36 -04:00
for ( ModContainer mod : mods ) {
if ( mod . wantsPostInit ()) {
2012-03-30 01:12:59 -04:00
LOG . finer ( String . format ( "Post-initializing %s" , mod . getName ()));
2012-03-28 16:44:36 -04:00
mod . postInit ();
}
}
}
private void load () {
File modsDir = new File ( "." , "mods" );
String canonicalModsPath ;
try {
canonicalModsPath = modsDir . getCanonicalPath ();
} catch ( IOException ioe ) {
2012-03-30 01:12:59 -04:00
LOG . severe ( String . format ( "Failed to resolve mods directory mods %s" , modsDir . getAbsolutePath ()));
2012-03-28 16:44:36 -04:00
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 );
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 ()) {
LOG . info ( String . format ( "Found directory %s. Attempting load" , modFile . getName ()));
attemptDirLoad ( modFile );
2012-03-30 01:12:59 -04:00
LOG . info ( String . format ( "Directory %s loaded successfully" , modFile . getName ()));
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-28 16:44:36 -04:00
LOG . info ( String . format ( "Found zip or jar file %s. Attempting load." , matcher . group ( 0 )));
attemptFileLoad ( modFile );
2012-03-30 01:12:59 -04:00
LOG . info ( String . format ( "File %s loaded successfully." , matcher . group ( 0 )));
2012-03-28 16:44:36 -04:00
}
}
}
2012-03-30 01:12:59 -04:00
if ( state == State . ERRORED ) {
2012-03-28 16:44:36 -04:00
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 );
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 );
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 01:12:59 -04:00
LOG . fine ( String . format ( "Successfully loaded mod class %s" , modClassFile . getName ()));
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
System . out . printf ( "Loading %s\n" , clazzName );
Class <?> clazz = Class . forName ( clazzName , false , modClassLoader );
System . out . printf ( "Got %s\n" , clazz );
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 )) {
LOG . fine ( String . format ( "ModLoader BaseMod class found: %s. Loading" , clazzName ));
@SuppressWarnings ( "unchecked" )
Class <? extends BaseMod > bmClazz = ( Class <? extends BaseMod > ) clazz ;
ModContainer mc = new ModLoaderModContainer ( bmClazz );
mods . add ( mc );
LOG . fine ( String . format ( "ModLoader BaseMod class loaded: %s." , clazzName ));
2012-03-28 16:44:36 -04:00
} else {
// Unrecognized
}
2012-03-30 01:12:59 -04:00
} catch ( Exception e ) {
LOG . warning ( String . format ( "Failed to load mod class %s in %s" , classFileName , classSource . getName ()));
LOG . throwing ( "fml.server.Loader" , "attemptLoad" , e );
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 );
}
}
private void attemptFileLoad ( File modFile ) {
extendClassLoader ( modFile );
2012-03-30 01:12:59 -04:00
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 );
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 01:12:59 -04:00
LOG . fine ( String . format ( "Mod class %s loaded successfully" , clazzName , modFile . getName ()));
2012-03-28 16:44:36 -04:00
}
}
} catch ( Exception e ) {
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-30 01:12:59 -04:00
public static List < ModContainer > getModList () {
return 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 > ();
load ();
sortModList ();
// Make mod list immutable
mods = Collections . unmodifiableList ( mods );
preModInit ();
}
public void initializeMods () {
modInit ();
postModInit ();
state = State . UP ;
}
2012-03-28 16:44:36 -04:00
}