2013-03-08 00:25:48 +00:00
/ *
* Forge Mod Loader
* Copyright ( c ) 2012 - 2013 cpw .
* All rights reserved . This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2 . 1
* which accompanies this distribution , and is available at
* http : //www.gnu.org/licenses/old-licenses/gpl-2.0.html
2013-03-08 00:37:52 +00:00
*
2013-03-08 00:25:48 +00:00
* Contributors :
* cpw - implementation
* /
2012-07-14 17:58:46 +00:00
package cpw.mods.fml.common ;
2012-10-16 16:39:04 +00:00
import java.lang.reflect.InvocationTargetException ;
2013-06-04 16:52:30 +00:00
import java.util.Collection ;
2012-07-24 01:20:37 +00:00
import java.util.List ;
2012-07-23 19:03:17 +00:00
import java.util.Map.Entry ;
2013-12-16 16:47:48 +00:00
import org.apache.logging.log4j.Level ;
2012-07-22 14:26:38 +00:00
import com.google.common.base.Joiner ;
2012-07-23 19:03:17 +00:00
import com.google.common.collect.ArrayListMultimap ;
2012-08-11 06:43:04 +00:00
import com.google.common.collect.BiMap ;
2013-06-04 16:52:30 +00:00
import com.google.common.collect.Collections2 ;
2012-08-11 06:43:04 +00:00
import com.google.common.collect.ImmutableBiMap ;
2012-07-22 14:26:38 +00:00
import com.google.common.collect.ImmutableMap ;
2012-08-17 13:24:38 +00:00
import com.google.common.collect.ImmutableMap.Builder ;
2012-07-24 01:20:37 +00:00
import com.google.common.collect.Iterables ;
import com.google.common.collect.Lists ;
2012-07-22 14:26:38 +00:00
import com.google.common.collect.Multimap ;
import com.google.common.eventbus.EventBus ;
2012-07-14 17:58:46 +00:00
import com.google.common.eventbus.Subscribe ;
2012-07-22 14:26:38 +00:00
import cpw.mods.fml.common.LoaderState.ModState ;
2012-10-24 20:39:55 +00:00
import cpw.mods.fml.common.event.FMLEvent ;
2012-08-04 15:26:51 +00:00
import cpw.mods.fml.common.event.FMLLoadEvent ;
2012-08-11 06:43:04 +00:00
import cpw.mods.fml.common.event.FMLPreInitializationEvent ;
2012-07-14 17:58:46 +00:00
import cpw.mods.fml.common.event.FMLStateEvent ;
2013-06-04 16:52:30 +00:00
import cpw.mods.fml.common.functions.ArtifactVersionNameFunction ;
import cpw.mods.fml.common.versioning.ArtifactVersion ;
2012-07-14 17:58:46 +00:00
public class LoadController
{
2012-07-22 14:26:38 +00:00
private Loader loader ;
private EventBus masterChannel ;
private ImmutableMap < String , EventBus > eventChannels ;
private LoaderState state ;
2012-07-23 19:03:17 +00:00
private Multimap < String , ModState > modStates = ArrayListMultimap . create ( ) ;
private Multimap < String , Throwable > errors = ArrayListMultimap . create ( ) ;
2012-07-24 01:20:37 +00:00
private List < ModContainer > activeModList = Lists . newArrayList ( ) ;
2012-08-24 16:10:43 +00:00
private ModContainer activeContainer ;
2012-08-11 06:43:04 +00:00
private BiMap < ModContainer , Object > modObjectList ;
2012-07-30 04:33:21 +00:00
2012-07-22 14:26:38 +00:00
public LoadController ( Loader loader )
{
this . loader = loader ;
this . masterChannel = new EventBus ( " FMLMainChannel " ) ;
this . masterChannel . register ( this ) ;
2012-07-30 04:33:21 +00:00
2012-08-04 15:26:51 +00:00
state = LoaderState . NOINIT ;
2012-08-11 15:44:55 +00:00
2012-08-04 15:26:51 +00:00
}
@Subscribe
public void buildModList ( FMLLoadEvent event )
{
2012-07-22 14:26:38 +00:00
Builder < String , EventBus > eventBus = ImmutableMap . builder ( ) ;
2012-07-30 04:33:21 +00:00
2012-07-24 01:20:37 +00:00
for ( ModContainer mod : loader . getModList ( ) )
2012-07-22 14:26:38 +00:00
{
2013-10-20 01:24:24 +00:00
//Create mod logger, and make the EventBus logger a child of it.
2012-07-22 14:26:38 +00:00
EventBus bus = new EventBus ( mod . getModId ( ) ) ;
boolean isActive = mod . registerBus ( bus , this ) ;
if ( isActive )
{
2012-07-24 01:20:37 +00:00
activeModList . add ( mod ) ;
2012-07-22 14:26:38 +00:00
modStates . put ( mod . getModId ( ) , ModState . UNLOADED ) ;
eventBus . put ( mod . getModId ( ) , bus ) ;
2013-07-02 05:39:40 +00:00
FMLCommonHandler . instance ( ) . addModToResourcePack ( mod ) ;
2012-07-22 14:26:38 +00:00
}
else
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( mod . getModId ( ) , Level . WARN , " Mod %s has been disabled through configuration " , mod . getModId ( ) ) ;
2012-07-22 14:26:38 +00:00
modStates . put ( mod . getModId ( ) , ModState . UNLOADED ) ;
modStates . put ( mod . getModId ( ) , ModState . DISABLED ) ;
}
}
2012-07-30 04:33:21 +00:00
2012-07-22 14:26:38 +00:00
eventChannels = eventBus . build ( ) ;
2013-07-10 04:42:40 +00:00
FMLCommonHandler . instance ( ) . updateResourcePackList ( ) ;
2012-07-22 14:26:38 +00:00
}
2012-07-14 17:58:46 +00:00
2012-08-02 04:38:30 +00:00
public void distributeStateMessage ( LoaderState state , Object . . . eventData )
2012-07-22 14:26:38 +00:00
{
if ( state . hasEvent ( ) )
{
masterChannel . post ( state . getEvent ( eventData ) ) ;
}
}
2012-07-30 04:33:21 +00:00
2013-06-11 18:41:19 +00:00
public void transition ( LoaderState desiredState , boolean forceState )
2012-07-22 14:26:38 +00:00
{
2012-07-23 19:03:17 +00:00
LoaderState oldState = state ;
2012-07-22 14:26:38 +00:00
state = state . transition ( ! errors . isEmpty ( ) ) ;
2013-06-11 18:41:19 +00:00
if ( state ! = desiredState & & ! forceState )
2012-07-23 19:03:17 +00:00
{
2012-10-16 16:39:04 +00:00
Throwable toThrow = null ;
2012-07-23 19:03:17 +00:00
FMLLog . severe ( " Fatal errors were detected during the transition from %s to %s. Loading cannot continue " , oldState , desiredState ) ;
StringBuilder sb = new StringBuilder ( ) ;
printModStates ( sb ) ;
2013-12-16 16:47:48 +00:00
FMLLog . severe ( sb . toString ( ) ) ;
2013-01-30 23:57:21 +00:00
if ( errors . size ( ) > 0 )
2012-07-23 19:03:17 +00:00
{
2013-01-30 23:57:21 +00:00
FMLLog . severe ( " The following problems were captured during this phase " ) ;
for ( Entry < String , Throwable > error : errors . entries ( ) )
2012-10-16 16:39:04 +00:00
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , error . getValue ( ) , " Caught exception from %s " , error . getKey ( ) ) ;
2013-01-30 23:57:21 +00:00
if ( error . getValue ( ) instanceof IFMLHandledException )
{
toThrow = error . getValue ( ) ;
}
else if ( toThrow = = null )
{
toThrow = error . getValue ( ) ;
}
2012-10-16 16:39:04 +00:00
}
}
2013-01-30 23:57:21 +00:00
else
{
FMLLog . severe ( " The ForgeModLoader state engine has become corrupted. Probably, a state was missed by and invalid modification to a base class " +
" ForgeModLoader depends on. This is a critical error and not recoverable. Investigate any modifications to base classes outside of " +
" ForgeModLoader, especially Optifine, to see if there are fixes available. " ) ;
throw new RuntimeException ( " The ForgeModLoader state engine is invalid " ) ;
}
2012-10-16 16:39:04 +00:00
if ( toThrow ! = null & & toThrow instanceof RuntimeException )
{
throw ( RuntimeException ) toThrow ;
}
else
{
throw new LoaderException ( toThrow ) ;
2012-07-23 19:03:17 +00:00
}
}
2013-06-11 18:41:19 +00:00
else if ( state ! = desiredState & & forceState )
{
FMLLog . info ( " The state engine was in incorrect state %s and forced into state %s. Errors may have been discarded. " , state , desiredState ) ;
forceState ( desiredState ) ;
}
2012-07-22 14:26:38 +00:00
}
2012-07-30 04:33:21 +00:00
2012-07-24 01:20:37 +00:00
public ModContainer activeContainer ( )
{
2012-08-24 16:10:43 +00:00
return activeContainer ;
2012-07-24 01:20:37 +00:00
}
2012-08-11 06:43:04 +00:00
2012-07-14 17:58:46 +00:00
@Subscribe
2012-10-24 20:39:55 +00:00
public void propogateStateMessage ( FMLEvent stateEvent )
2012-07-22 14:26:38 +00:00
{
2012-08-11 20:21:03 +00:00
if ( stateEvent instanceof FMLPreInitializationEvent )
{
2012-08-16 00:05:12 +00:00
modObjectList = buildModObjectList ( ) ;
2012-08-11 20:21:03 +00:00
}
2012-08-24 16:10:43 +00:00
for ( ModContainer mc : activeModList )
2012-07-22 14:26:38 +00:00
{
2013-06-04 16:52:30 +00:00
sendEventToModContainer ( stateEvent , mc ) ;
}
}
private void sendEventToModContainer ( FMLEvent stateEvent , ModContainer mc )
{
String modId = mc . getModId ( ) ;
Collection < String > requirements = Collections2 . transform ( mc . getRequirements ( ) , new ArtifactVersionNameFunction ( ) ) ;
for ( ArtifactVersion av : mc . getDependencies ( ) )
{
if ( av . getLabel ( ) ! = null & & requirements . contains ( av . getLabel ( ) ) & & modStates . containsEntry ( av . getLabel ( ) , ModState . ERRORED ) )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( modId , Level . ERROR , " Skipping event %s and marking errored mod %s since required dependency %s has errored " , stateEvent . getEventType ( ) , modId , av . getLabel ( ) ) ;
2013-06-04 16:52:30 +00:00
modStates . put ( modId , ModState . ERRORED ) ;
return ;
}
}
activeContainer = mc ;
stateEvent . applyModContainer ( activeContainer ( ) ) ;
2013-12-16 16:47:48 +00:00
FMLLog . log ( modId , Level . TRACE , " Sending event %s to mod %s " , stateEvent . getEventType ( ) , modId ) ;
2013-06-04 16:52:30 +00:00
eventChannels . get ( modId ) . post ( stateEvent ) ;
2013-12-16 16:47:48 +00:00
FMLLog . log ( modId , Level . TRACE , " Sent event %s to mod %s " , stateEvent . getEventType ( ) , modId ) ;
2013-06-04 16:52:30 +00:00
activeContainer = null ;
if ( stateEvent instanceof FMLStateEvent )
{
if ( ! errors . containsKey ( modId ) )
{
modStates . put ( modId , ( ( FMLStateEvent ) stateEvent ) . getModState ( ) ) ;
}
else
2012-07-22 14:26:38 +00:00
{
2013-06-04 16:52:30 +00:00
modStates . put ( modId , ModState . ERRORED ) ;
2012-07-22 14:26:38 +00:00
}
}
}
2012-08-16 00:05:12 +00:00
public ImmutableBiMap < ModContainer , Object > buildModObjectList ( )
2012-08-11 06:43:04 +00:00
{
2012-08-11 15:44:55 +00:00
ImmutableBiMap . Builder < ModContainer , Object > builder = ImmutableBiMap . < ModContainer , Object > builder ( ) ;
2012-08-11 06:43:04 +00:00
for ( ModContainer mc : activeModList )
{
2012-08-16 00:05:12 +00:00
if ( ! mc . isImmutable ( ) & & mc . getMod ( ) ! = null )
2012-08-11 20:21:03 +00:00
{
builder . put ( mc , mc . getMod ( ) ) ;
}
2012-08-16 17:55:42 +00:00
if ( mc . getMod ( ) = = null & & ! mc . isImmutable ( ) & & state ! = LoaderState . CONSTRUCTING )
2012-08-16 00:05:12 +00:00
{
FMLLog . severe ( " There is a severe problem with %s - it appears not to have constructed correctly " , mc . getModId ( ) ) ;
if ( state ! = LoaderState . CONSTRUCTING )
{
this . errorOccurred ( mc , new RuntimeException ( ) ) ;
}
}
2012-08-11 06:43:04 +00:00
}
2012-08-16 00:05:12 +00:00
return builder . build ( ) ;
2012-08-11 06:43:04 +00:00
}
2012-08-11 15:44:55 +00:00
2012-07-22 14:26:38 +00:00
public void errorOccurred ( ModContainer modContainer , Throwable exception )
2012-07-14 17:58:46 +00:00
{
2012-10-16 16:39:04 +00:00
if ( exception instanceof InvocationTargetException )
{
errors . put ( modContainer . getModId ( ) , ( ( InvocationTargetException ) exception ) . getCause ( ) ) ;
}
else
{
errors . put ( modContainer . getModId ( ) , exception ) ;
}
2012-07-22 14:26:38 +00:00
}
public void printModStates ( StringBuilder ret )
{
2012-08-24 16:10:43 +00:00
for ( ModContainer mc : loader . getModList ( ) )
2012-07-22 14:26:38 +00:00
{
2013-04-16 17:30:39 +00:00
ret . append ( " \ n \ t " ) . append ( mc . getModId ( ) ) . append ( " { " ) . append ( mc . getVersion ( ) ) . append ( " } [ " ) . append ( mc . getName ( ) ) . append ( " ] ( " ) . append ( mc . getSource ( ) . getName ( ) ) . append ( " ) " ) ;
2012-08-24 16:10:43 +00:00
Joiner . on ( " -> " ) . appendTo ( ret , modStates . get ( mc . getModId ( ) ) ) ;
2012-07-22 14:26:38 +00:00
}
2012-07-14 17:58:46 +00:00
}
2012-07-24 01:20:37 +00:00
public List < ModContainer > getActiveModList ( )
{
return activeModList ;
}
public ModState getModState ( ModContainer selectedMod )
{
return Iterables . getLast ( modStates . get ( selectedMod . getModId ( ) ) , ModState . AVAILABLE ) ;
}
2012-08-04 15:26:51 +00:00
public void distributeStateMessage ( Class < ? > customEvent )
{
try
{
masterChannel . post ( customEvent . newInstance ( ) ) ;
}
catch ( Exception e )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , e , " An unexpected exception " ) ;
2012-08-04 15:26:51 +00:00
throw new LoaderException ( e ) ;
}
}
2012-08-11 06:43:04 +00:00
public BiMap < ModContainer , Object > getModObjectList ( )
{
2012-08-16 00:05:12 +00:00
if ( modObjectList = = null )
{
FMLLog . severe ( " Detected an attempt by a mod %s to perform game activity during mod construction. This is a serious programming error. " , activeContainer ) ;
return buildModObjectList ( ) ;
}
2012-08-11 06:43:04 +00:00
return ImmutableBiMap . copyOf ( modObjectList ) ;
}
2012-08-13 19:26:29 +00:00
public boolean isInState ( LoaderState state )
{
return this . state = = state ;
}
2012-10-24 20:39:55 +00:00
boolean hasReachedState ( LoaderState state ) {
return this . state . ordinal ( ) > = state . ordinal ( ) & & this . state ! = LoaderState . ERRORED ;
}
2013-03-08 00:37:52 +00:00
void forceState ( LoaderState newState )
{
this . state = newState ;
}
2012-07-14 17:58:46 +00:00
}