2012-03-30 14:11:13 +00:00
/ *
2012-04-02 22:09:06 +00:00
* The FML Forge Mod Loader suite . Copyright ( C ) 2012 cpw
2012-04-05 14:07:52 +00:00
*
2012-03-30 14:11:13 +00:00
* 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 .
2012-04-05 14:07:52 +00:00
*
2012-03-30 14:11:13 +00:00
* 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 .
2012-04-05 14:07:52 +00:00
*
2012-03-30 14:11:13 +00:00
* 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
* /
2012-04-02 22:09:06 +00:00
package cpw.mods.fml.common ;
2012-03-30 14:11:13 +00:00
2012-04-18 18:03:30 +00:00
import java.io.File ;
2012-09-17 01:05:11 +00:00
import java.io.FileInputStream ;
2012-07-23 19:03:17 +00:00
import java.lang.annotation.Annotation ;
import java.lang.reflect.Field ;
import java.lang.reflect.Method ;
2012-08-26 14:45:00 +00:00
import java.lang.reflect.Modifier ;
2012-07-30 04:33:21 +00:00
import java.util.Arrays ;
2012-04-03 03:06:30 +00:00
import java.util.List ;
2012-05-11 21:31:28 +00:00
import java.util.Map ;
2012-09-17 01:05:11 +00:00
import java.util.Properties ;
2012-08-23 17:43:25 +00:00
import java.util.Set ;
2012-08-26 14:45:00 +00:00
import java.util.logging.Level ;
2012-09-17 01:05:11 +00:00
import java.util.zip.ZipEntry ;
import java.util.zip.ZipFile ;
import java.util.zip.ZipInputStream ;
2012-04-03 03:06:30 +00:00
2012-08-26 14:45:00 +00:00
import com.google.common.base.Function ;
2012-08-14 17:36:29 +00:00
import com.google.common.base.Strings ;
2012-07-22 14:26:38 +00:00
import com.google.common.base.Throwables ;
2012-07-23 19:03:17 +00:00
import com.google.common.collect.ArrayListMultimap ;
2012-08-02 12:01:03 +00:00
import com.google.common.collect.BiMap ;
import com.google.common.collect.ImmutableBiMap ;
2012-07-22 14:26:38 +00:00
import com.google.common.collect.Lists ;
2012-07-23 19:03:17 +00:00
import com.google.common.collect.Multimap ;
2012-08-26 14:45:00 +00:00
import com.google.common.collect.SetMultimap ;
2012-08-23 17:43:25 +00:00
import com.google.common.collect.Sets ;
2012-07-22 14:26:38 +00:00
import com.google.common.eventbus.EventBus ;
import com.google.common.eventbus.Subscribe ;
2012-07-23 19:03:17 +00:00
import cpw.mods.fml.common.Mod.Instance ;
import cpw.mods.fml.common.Mod.Metadata ;
2012-08-06 13:52:42 +00:00
import cpw.mods.fml.common.discovery.ASMDataTable ;
2012-08-26 14:45:00 +00:00
import cpw.mods.fml.common.discovery.ASMDataTable.ASMData ;
2012-07-22 14:26:38 +00:00
import cpw.mods.fml.common.event.FMLConstructionEvent ;
2012-07-23 19:03:17 +00:00
import cpw.mods.fml.common.event.FMLInitializationEvent ;
import cpw.mods.fml.common.event.FMLPostInitializationEvent ;
import cpw.mods.fml.common.event.FMLPreInitializationEvent ;
2012-08-02 12:01:03 +00:00
import cpw.mods.fml.common.event.FMLServerStartedEvent ;
import cpw.mods.fml.common.event.FMLServerStartingEvent ;
import cpw.mods.fml.common.event.FMLServerStoppingEvent ;
import cpw.mods.fml.common.event.FMLStateEvent ;
2012-08-06 20:12:50 +00:00
import cpw.mods.fml.common.network.FMLNetworkHandler ;
2012-07-30 21:01:27 +00:00
import cpw.mods.fml.common.versioning.ArtifactVersion ;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion ;
2012-07-22 14:26:38 +00:00
2012-04-05 14:07:52 +00:00
public class FMLModContainer implements ModContainer
{
private Mod modDescriptor ;
private Object modInstance ;
2012-04-18 18:03:30 +00:00
private File source ;
2012-05-15 20:19:46 +00:00
private ModMetadata modMetadata ;
2012-07-22 14:26:38 +00:00
private String className ;
private Map < String , Object > descriptor ;
2012-07-23 19:03:17 +00:00
private boolean enabled = true ;
2012-08-26 14:45:00 +00:00
private String internalVersion ;
2012-07-22 14:26:38 +00:00
private boolean overridesMetadata ;
private EventBus eventBus ;
private LoadController controller ;
2012-07-23 19:03:17 +00:00
private Multimap < Class < ? extends Annotation > , Object > annotations ;
2012-07-30 21:01:27 +00:00
private DefaultArtifactVersion processedVersion ;
2012-08-06 13:52:42 +00:00
private boolean isNetworkMod ;
2012-04-05 14:07:52 +00:00
2012-08-02 12:01:03 +00:00
private static final BiMap < Class < ? extends FMLStateEvent > , Class < ? extends Annotation > > modAnnotationTypes = ImmutableBiMap . < Class < ? extends FMLStateEvent > , Class < ? extends Annotation > > builder ( )
. put ( FMLPreInitializationEvent . class , Mod . PreInit . class )
. put ( FMLInitializationEvent . class , Mod . Init . class )
. put ( FMLPostInitializationEvent . class , Mod . PostInit . class )
. put ( FMLServerStartingEvent . class , Mod . ServerStarting . class )
. put ( FMLServerStartedEvent . class , Mod . ServerStarted . class )
. put ( FMLServerStoppingEvent . class , Mod . ServerStopping . class )
. build ( ) ;
private static final BiMap < Class < ? extends Annotation > , Class < ? extends FMLStateEvent > > modTypeAnnotations = modAnnotationTypes . inverse ( ) ;
2012-08-16 17:55:42 +00:00
private String annotationDependencies ;
2012-08-04 15:26:51 +00:00
2012-07-22 14:26:38 +00:00
public FMLModContainer ( String className , File modSource , Map < String , Object > modDescriptor )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
this . className = className ;
this . source = modSource ;
this . descriptor = modDescriptor ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-22 14:26:38 +00:00
public String getModId ( )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return ( String ) descriptor . get ( " modid " ) ;
2012-04-05 14:07:52 +00:00
}
@Override
public String getName ( )
{
2012-07-22 14:26:38 +00:00
return modMetadata . name ;
2012-05-10 13:42:31 +00:00
}
2012-04-05 14:07:52 +00:00
@Override
2012-07-22 14:26:38 +00:00
public String getVersion ( )
2012-04-05 14:07:52 +00:00
{
2012-08-26 14:45:00 +00:00
return internalVersion ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-04-18 18:03:30 +00:00
public File getSource ( )
2012-04-05 14:07:52 +00:00
{
return source ;
}
@Override
2012-07-22 14:26:38 +00:00
public ModMetadata getMetadata ( )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return modMetadata ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-22 14:26:38 +00:00
public void bindMetadata ( MetadataCollection mc )
2012-04-05 14:07:52 +00:00
{
2012-07-23 19:03:17 +00:00
modMetadata = mc . getMetadataForId ( getModId ( ) , descriptor ) ;
2012-07-30 04:33:21 +00:00
2012-07-22 14:26:38 +00:00
if ( descriptor . containsKey ( " usesMetadata " ) )
{
2012-07-30 04:33:21 +00:00
overridesMetadata = ! ( ( Boolean ) descriptor . get ( " usesMetadata " ) ) . booleanValue ( ) ;
2012-07-22 14:26:38 +00:00
}
2012-07-30 04:33:21 +00:00
2012-07-22 14:26:38 +00:00
if ( overridesMetadata | | ! modMetadata . useDependencyInformation )
{
2012-08-23 17:43:25 +00:00
Set < ArtifactVersion > requirements = Sets . newHashSet ( ) ;
2012-07-30 21:01:27 +00:00
List < ArtifactVersion > dependencies = Lists . newArrayList ( ) ;
List < ArtifactVersion > dependants = Lists . newArrayList ( ) ;
2012-08-16 17:55:42 +00:00
annotationDependencies = ( String ) descriptor . get ( " dependencies " ) ;
Loader . instance ( ) . computeDependencies ( annotationDependencies , requirements , dependencies , dependants ) ;
2012-07-22 14:26:38 +00:00
modMetadata . requiredMods = requirements ;
modMetadata . dependencies = dependencies ;
modMetadata . dependants = dependants ;
2012-08-23 17:43:25 +00:00
FMLLog . finest ( " Parsed dependency info : %s %s %s " , requirements , dependencies , dependants ) ;
2012-07-22 14:26:38 +00:00
}
2012-08-23 20:46:23 +00:00
else
{
FMLLog . finest ( " Using mcmod dependency info : %s %s %s " , modMetadata . requiredMods , modMetadata . dependencies , modMetadata . dependants ) ;
}
2012-08-14 17:36:29 +00:00
if ( Strings . isNullOrEmpty ( modMetadata . name ) )
{
2012-08-23 20:46:23 +00:00
FMLLog . info ( " Mod %s is missing the required element 'name'. Substituting %s " , getModId ( ) , getModId ( ) ) ;
2012-08-14 17:36:29 +00:00
modMetadata . name = getModId ( ) ;
}
2012-08-26 14:45:00 +00:00
internalVersion = ( String ) descriptor . get ( " version " ) ;
2012-09-17 01:05:11 +00:00
if ( Strings . isNullOrEmpty ( internalVersion ) & & getSource ( ) . isFile ( ) )
{
2012-09-17 01:42:59 +00:00
Properties versionProps = searchForVersionProperties ( ) ;
if ( versionProps ! = null )
{
internalVersion = versionProps . getProperty ( getModId ( ) + " .version " ) ;
FMLLog . fine ( " Found version %s for mod %s in version.properties " , internalVersion , getModId ( ) ) ;
}
2012-09-17 01:05:11 +00:00
}
2012-08-26 14:45:00 +00:00
if ( Strings . isNullOrEmpty ( internalVersion ) & & ! Strings . isNullOrEmpty ( modMetadata . version ) )
{
2012-09-17 01:42:59 +00:00
FMLLog . warning ( " Mod %s is missing the required element 'version' and a version.properties file could not be found. Falling back to metadata version %s " , getModId ( ) , modMetadata . version ) ;
2012-09-17 00:56:19 +00:00
internalVersion = modMetadata . version ;
2012-08-26 14:45:00 +00:00
}
if ( Strings . isNullOrEmpty ( internalVersion ) )
2012-08-23 17:43:25 +00:00
{
2012-08-26 14:45:00 +00:00
FMLLog . warning ( " Mod %s is missing the required element 'version' and no fallback can be found. Substituting '1.0'. " , getModId ( ) ) ;
modMetadata . version = internalVersion = " 1.0 " ;
2012-08-23 17:43:25 +00:00
}
2012-04-05 14:07:52 +00:00
}
2012-09-17 01:42:59 +00:00
public Properties searchForVersionProperties ( )
2012-09-17 01:05:11 +00:00
{
try
{
2012-09-17 01:42:59 +00:00
FMLLog . fine ( " Attempting to load the file version.properties from %s to locate a version number for %s " , getSource ( ) . getName ( ) , getModId ( ) ) ;
Properties version = null ;
if ( getSource ( ) . isFile ( ) )
{
ZipFile source = new ZipFile ( getSource ( ) ) ;
ZipEntry versionFile = source . getEntry ( " version.properties " ) ;
if ( versionFile ! = null )
{
version = new Properties ( ) ;
version . load ( source . getInputStream ( versionFile ) ) ;
}
source . close ( ) ;
}
else if ( getSource ( ) . isDirectory ( ) )
2012-09-17 01:05:11 +00:00
{
2012-09-17 01:42:59 +00:00
File propsFile = new File ( getSource ( ) , " version.properties " ) ;
if ( propsFile . exists ( ) & & propsFile . isFile ( ) )
{
version = new Properties ( ) ;
FileInputStream fis = new FileInputStream ( propsFile ) ;
version . load ( fis ) ;
fis . close ( ) ;
}
2012-09-17 01:05:11 +00:00
}
return version ;
}
catch ( Exception e )
{
Throwables . propagateIfPossible ( e ) ;
FMLLog . fine ( " Failed to find a usable version.properties file " ) ;
return null ;
}
}
2012-04-05 14:07:52 +00:00
@Override
2012-07-22 14:26:38 +00:00
public void setEnabledState ( boolean enabled )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
this . enabled = enabled ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-08-23 17:43:25 +00:00
public Set < ArtifactVersion > getRequirements ( )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return modMetadata . requiredMods ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-30 21:01:27 +00:00
public List < ArtifactVersion > getDependencies ( )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return modMetadata . dependencies ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-30 21:01:27 +00:00
public List < ArtifactVersion > getDependants ( )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return modMetadata . dependants ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-22 14:26:38 +00:00
public String getSortingRules ( )
2012-04-05 14:07:52 +00:00
{
2012-08-23 18:28:49 +00:00
return ( ( overridesMetadata | | ! modMetadata . useDependencyInformation ) ? Strings . nullToEmpty ( annotationDependencies ) : modMetadata . printableSortingRules ( ) ) ;
2012-04-05 14:07:52 +00:00
}
@Override
2012-07-22 14:26:38 +00:00
public boolean matches ( Object mod )
2012-04-05 14:07:52 +00:00
{
2012-07-22 14:26:38 +00:00
return mod = = modInstance ;
2012-04-05 14:07:52 +00:00
}
2012-04-08 06:00:57 +00:00
@Override
2012-07-22 14:26:38 +00:00
public Object getMod ( )
2012-04-08 06:00:57 +00:00
{
2012-07-22 14:26:38 +00:00
return modInstance ;
2012-04-08 06:00:57 +00:00
}
2012-04-09 14:18:24 +00:00
2012-06-02 19:13:55 +00:00
@Override
2012-07-22 14:26:38 +00:00
public boolean registerBus ( EventBus bus , LoadController controller )
2012-06-02 19:13:55 +00:00
{
2012-07-22 14:26:38 +00:00
if ( this . enabled )
{
2012-07-23 19:03:17 +00:00
FMLLog . fine ( " Enabling mod %s " , getModId ( ) ) ;
2012-07-22 14:26:38 +00:00
this . eventBus = bus ;
this . controller = controller ;
eventBus . register ( this ) ;
return true ;
}
else
{
return false ;
}
2012-06-02 19:13:55 +00:00
}
2012-07-30 04:33:21 +00:00
2012-07-23 19:03:17 +00:00
private Multimap < Class < ? extends Annotation > , Object > gatherAnnotations ( Class < ? > clazz ) throws Exception
{
Multimap < Class < ? extends Annotation > , Object > anns = ArrayListMultimap . create ( ) ;
2012-07-30 04:33:21 +00:00
2012-07-23 19:03:17 +00:00
for ( Method m : clazz . getDeclaredMethods ( ) )
{
for ( Annotation a : m . getAnnotations ( ) )
{
2012-08-13 20:15:11 +00:00
if ( modTypeAnnotations . containsKey ( a . annotationType ( ) ) )
2012-07-23 19:03:17 +00:00
{
2012-08-13 20:15:11 +00:00
Class < ? > [ ] paramTypes = new Class [ ] { modTypeAnnotations . get ( a . annotationType ( ) ) } ;
if ( Arrays . equals ( m . getParameterTypes ( ) , paramTypes ) )
{
m . setAccessible ( true ) ;
anns . put ( a . annotationType ( ) , m ) ;
}
else
{
FMLLog . severe ( " The mod %s appears to have an invalid method annotation %s. This annotation can only apply to methods with argument types %s -it will not be called " , getModId ( ) , a . annotationType ( ) . getSimpleName ( ) , Arrays . toString ( paramTypes ) ) ;
}
2012-07-23 19:03:17 +00:00
}
}
}
return anns ;
}
2012-08-26 14:45:00 +00:00
private void processFieldAnnotations ( ASMDataTable asmDataTable ) throws Exception
2012-07-23 19:03:17 +00:00
{
2012-08-26 14:45:00 +00:00
SetMultimap < String , ASMData > annotations = asmDataTable . getAnnotationsFor ( this ) ;
2012-07-30 04:33:21 +00:00
2012-08-26 14:45:00 +00:00
parseSimpleFieldAnnotation ( annotations , Instance . class . getName ( ) , new Function < ModContainer , Object > ( )
2012-07-23 19:03:17 +00:00
{
2012-08-26 14:45:00 +00:00
public Object apply ( ModContainer mc )
{
return mc . getMod ( ) ;
}
} ) ;
parseSimpleFieldAnnotation ( annotations , Metadata . class . getName ( ) , new Function < ModContainer , Object > ( )
{
public Object apply ( ModContainer mc )
{
return mc . getMetadata ( ) ;
}
} ) ;
2012-08-02 12:01:03 +00:00
//TODO
// for (Object o : annotations.get(Block.class))
// {
// Field f = (Field) o;
// f.set(modInstance, GameRegistry.buildBlock(this, f.getType(), f.getAnnotation(Block.class)));
// }
2012-07-23 19:03:17 +00:00
}
2012-08-26 14:45:00 +00:00
private void parseSimpleFieldAnnotation ( SetMultimap < String , ASMData > annotations , String annotationClassName , Function < ModContainer , Object > retreiver ) throws IllegalAccessException
{
String [ ] annName = annotationClassName . split ( " \\ . " ) ;
String annotationName = annName [ annName . length - 1 ] ;
for ( ASMData targets : annotations . get ( annotationClassName ) )
{
String targetMod = ( String ) targets . getAnnotationInfo ( ) . get ( " value " ) ;
Field f = null ;
Object injectedMod = null ;
ModContainer mc = this ;
boolean isStatic = false ;
Class < ? > clz = modInstance . getClass ( ) ;
if ( ! Strings . isNullOrEmpty ( targetMod ) )
{
2012-08-26 14:55:55 +00:00
if ( Loader . isModLoaded ( targetMod ) )
{
mc = Loader . instance ( ) . getIndexedModList ( ) . get ( targetMod ) ;
}
else
{
mc = null ;
}
2012-08-26 14:45:00 +00:00
}
if ( mc ! = null )
{
try
{
clz = Class . forName ( targets . getClassName ( ) , true , Loader . instance ( ) . getModClassLoader ( ) ) ;
f = clz . getDeclaredField ( targets . getObjectName ( ) ) ;
f . setAccessible ( true ) ;
isStatic = Modifier . isStatic ( f . getModifiers ( ) ) ;
injectedMod = retreiver . apply ( mc ) ;
}
catch ( Exception e )
{
Throwables . propagateIfPossible ( e ) ;
FMLLog . log ( Level . WARNING , e , " Attempting to load @%s in class %s for %s and failing " , annotationName , targets . getClassName ( ) , mc . getModId ( ) ) ;
}
}
if ( f ! = null )
{
Object target = null ;
if ( ! isStatic )
{
target = modInstance ;
if ( ! modInstance . getClass ( ) . equals ( clz ) )
{
FMLLog . warning ( " Unable to inject @%s in non-static field %s.%s for %s as it is NOT the primary mod instance " , annotationName , targets . getClassName ( ) , targets . getObjectName ( ) , mc . getModId ( ) ) ;
continue ;
}
}
f . set ( target , injectedMod ) ;
}
}
}
2012-07-22 14:26:38 +00:00
@Subscribe
2012-07-23 19:03:17 +00:00
public void constructMod ( FMLConstructionEvent event )
2012-06-06 08:43:49 +00:00
{
2012-07-30 04:33:21 +00:00
try
2012-07-22 14:26:38 +00:00
{
ModClassLoader modClassLoader = event . getModClassLoader ( ) ;
modClassLoader . addFile ( source ) ;
2012-07-23 19:03:17 +00:00
Class < ? > clazz = Class . forName ( className , true , modClassLoader ) ;
2012-08-06 13:52:42 +00:00
ASMDataTable asmHarvestedAnnotations = event . getASMHarvestedData ( ) ;
2012-08-10 21:42:43 +00:00
// TODO
2012-08-06 13:52:42 +00:00
asmHarvestedAnnotations . getAnnotationsFor ( this ) ;
2012-07-23 19:03:17 +00:00
annotations = gatherAnnotations ( clazz ) ;
2012-08-06 20:12:50 +00:00
isNetworkMod = FMLNetworkHandler . instance ( ) . registerNetworkMod ( this , clazz , event . getASMHarvestedData ( ) ) ;
2012-07-23 19:03:17 +00:00
modInstance = clazz . newInstance ( ) ;
2012-08-10 21:42:43 +00:00
ProxyInjector . inject ( this , event . getASMHarvestedData ( ) , FMLCommonHandler . instance ( ) . getSide ( ) ) ;
2012-08-26 14:45:00 +00:00
processFieldAnnotations ( event . getASMHarvestedData ( ) ) ;
2012-07-22 14:26:38 +00:00
}
2012-07-23 19:03:17 +00:00
catch ( Throwable e )
2012-07-22 14:26:38 +00:00
{
controller . errorOccurred ( this , e ) ;
Throwables . propagateIfPossible ( e ) ;
}
2012-06-06 08:43:49 +00:00
}
2012-07-30 04:33:21 +00:00
2012-07-23 19:03:17 +00:00
@Subscribe
2012-08-02 12:01:03 +00:00
public void handleModStateEvent ( FMLStateEvent event )
2012-07-23 19:03:17 +00:00
{
2012-08-02 12:01:03 +00:00
Class < ? extends Annotation > annotation = modAnnotationTypes . get ( event . getClass ( ) ) ;
if ( annotation = = null )
2012-07-23 19:03:17 +00:00
{
2012-08-02 12:01:03 +00:00
return ;
2012-07-23 19:03:17 +00:00
}
try
{
2012-08-02 12:01:03 +00:00
for ( Object o : annotations . get ( annotation ) )
2012-07-23 19:03:17 +00:00
{
Method m = ( Method ) o ;
2012-07-30 04:33:21 +00:00
m . invoke ( modInstance , event ) ;
2012-07-23 19:03:17 +00:00
}
}
catch ( Throwable t )
{
controller . errorOccurred ( this , t ) ;
Throwables . propagateIfPossible ( t ) ;
}
}
2012-07-30 21:01:27 +00:00
@Override
public ArtifactVersion getProcessedVersion ( )
{
if ( processedVersion = = null )
{
processedVersion = new DefaultArtifactVersion ( getModId ( ) , getVersion ( ) ) ;
}
return processedVersion ;
}
2012-08-04 15:26:51 +00:00
@Override
public boolean isImmutable ( )
{
return false ;
}
2012-08-06 13:52:42 +00:00
@Override
public boolean isNetworkMod ( )
{
return isNetworkMod ;
}
2012-08-26 14:45:00 +00:00
@Override
public String getDisplayVersion ( )
{
return modMetadata . version ;
}
2012-03-30 14:11:13 +00:00
}