2013-03-07 19:25:48 -05: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-07 19:37:52 -05:00
*
2013-03-07 19:25:48 -05:00
* Contributors:
* cpw - implementation
*/
2012-07-14 13:58:46 -04:00
package cpw.mods.fml.common ;
2012-10-16 12:39:04 -04:00
import java.lang.reflect.InvocationTargetException ;
2013-06-04 12:52:30 -04:00
import java.util.Collection ;
2013-12-17 10:51:00 -05:00
import java.util.HashMap ;
2012-07-23 22:50:37 -02:30
import java.util.List ;
2012-07-23 16:33:17 -02:30
import java.util.Map.Entry ;
2013-12-16 11:47:48 -05:00
import org.apache.logging.log4j.Level ;
2014-01-20 21:58:39 -05:00
import org.apache.logging.log4j.ThreadContext ;
2012-07-22 11:56:38 -02:30
import com.google.common.base.Joiner ;
2012-07-23 16:33:17 -02:30
import com.google.common.collect.ArrayListMultimap ;
2012-08-11 02:43:04 -04:00
import com.google.common.collect.BiMap ;
2013-06-04 12:52:30 -04:00
import com.google.common.collect.Collections2 ;
2012-08-11 02:43:04 -04:00
import com.google.common.collect.ImmutableBiMap ;
2012-07-22 11:56:38 -02:30
import com.google.common.collect.ImmutableMap ;
2012-08-17 09:24:38 -04:00
import com.google.common.collect.ImmutableMap.Builder ;
2012-07-23 22:50:37 -02:30
import com.google.common.collect.Iterables ;
2014-01-25 06:17:14 -05:00
import com.google.common.collect.ListMultimap ;
2012-07-23 22:50:37 -02:30
import com.google.common.collect.Lists ;
2013-12-17 10:51:00 -05:00
import com.google.common.collect.Maps ;
2012-07-22 11:56:38 -02:30
import com.google.common.collect.Multimap ;
import com.google.common.eventbus.EventBus ;
2012-07-14 13:58:46 -04:00
import com.google.common.eventbus.Subscribe ;
2012-07-22 11:56:38 -02:30
import cpw.mods.fml.common.LoaderState.ModState ;
2012-10-24 16:39:55 -04:00
import cpw.mods.fml.common.event.FMLEvent ;
2012-08-04 11:26:51 -04:00
import cpw.mods.fml.common.event.FMLLoadEvent ;
2013-12-17 10:51:00 -05:00
import cpw.mods.fml.common.event.FMLModDisabledEvent ;
2012-08-11 02:43:04 -04:00
import cpw.mods.fml.common.event.FMLPreInitializationEvent ;
2012-07-14 13:58:46 -04:00
import cpw.mods.fml.common.event.FMLStateEvent ;
2013-06-04 12:52:30 -04:00
import cpw.mods.fml.common.functions.ArtifactVersionNameFunction ;
import cpw.mods.fml.common.versioning.ArtifactVersion ;
2012-07-14 13:58:46 -04:00
public class LoadController
{
2012-07-22 11:56:38 -02:30
private Loader loader ;
private EventBus masterChannel ;
private ImmutableMap < String , EventBus > eventChannels ;
private LoaderState state ;
2012-07-23 16:33:17 -02:30
private Multimap < String , ModState > modStates = ArrayListMultimap . create ();
private Multimap < String , Throwable > errors = ArrayListMultimap . create ();
2012-07-23 22:50:37 -02:30
private List < ModContainer > activeModList = Lists . newArrayList ();
2012-08-24 12:10:43 -04:00
private ModContainer activeContainer ;
2012-08-11 02:43:04 -04:00
private BiMap < ModContainer , Object > modObjectList ;
2014-01-25 06:17:14 -05:00
private ListMultimap < String , ModContainer > packageOwners ;
2012-07-30 00:33:21 -04:00
2012-07-22 11:56:38 -02:30
public LoadController ( Loader loader )
{
this . loader = loader ;
this . masterChannel = new EventBus ( "FMLMainChannel" );
this . masterChannel . register ( this );
2012-07-30 00:33:21 -04:00
2012-08-04 11:26:51 -04:00
state = LoaderState . NOINIT ;
2014-01-25 06:17:14 -05:00
packageOwners = ArrayListMultimap . create ();
2012-08-11 11:44:55 -04:00
2012-08-04 11:26:51 -04:00
}
2013-12-17 10:51:00 -05:00
void disableMod ( ModContainer mod )
{
HashMap < String , EventBus > temporary = Maps . newHashMap ( eventChannels );
String modId = mod . getModId ();
EventBus bus = temporary . remove ( modId );
bus . post ( new FMLModDisabledEvent ());
if ( errors . get ( modId ). isEmpty ())
{
eventChannels = ImmutableMap . copyOf ( temporary );
modStates . put ( modId , ModState . DISABLED );
modObjectList . remove ( mod );
activeModList . remove ( mod );
}
}
2012-08-04 11:26:51 -04:00
@Subscribe
public void buildModList ( FMLLoadEvent event )
{
2012-07-22 11:56:38 -02:30
Builder < String , EventBus > eventBus = ImmutableMap . builder ();
2012-07-30 00:33:21 -04:00
2012-07-23 22:50:37 -02:30
for ( ModContainer mod : loader . getModList ())
2012-07-22 11:56:38 -02:30
{
2013-10-19 18:24:24 -07:00
//Create mod logger, and make the EventBus logger a child of it.
2012-07-22 11:56:38 -02:30
EventBus bus = new EventBus ( mod . getModId ());
boolean isActive = mod . registerBus ( bus , this );
if ( isActive )
{
2012-07-23 22:50:37 -02:30
activeModList . add ( mod );
2012-07-22 11:56:38 -02:30
modStates . put ( mod . getModId (), ModState . UNLOADED );
eventBus . put ( mod . getModId (), bus );
2013-07-02 01:39:40 -04:00
FMLCommonHandler . instance (). addModToResourcePack ( mod );
2012-07-22 11:56:38 -02:30
}
else
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( mod . getModId (), Level . WARN , "Mod %s has been disabled through configuration" , mod . getModId ());
2012-07-22 11:56:38 -02:30
modStates . put ( mod . getModId (), ModState . UNLOADED );
modStates . put ( mod . getModId (), ModState . DISABLED );
}
}
2012-07-30 00:33:21 -04:00
2012-07-22 11:56:38 -02:30
eventChannels = eventBus . build ();
2013-07-10 00:42:40 -04:00
FMLCommonHandler . instance (). updateResourcePackList ();
2012-07-22 11:56:38 -02:30
}
2012-07-14 13:58:46 -04:00
2012-08-02 02:08:30 -02:30
public void distributeStateMessage ( LoaderState state , Object ... eventData )
2012-07-22 11:56:38 -02:30
{
if ( state . hasEvent ())
{
masterChannel . post ( state . getEvent ( eventData ));
}
}
2012-07-30 00:33:21 -04:00
2013-06-11 14:41:19 -04:00
public void transition ( LoaderState desiredState , boolean forceState )
2012-07-22 11:56:38 -02:30
{
2012-07-23 16:33:17 -02:30
LoaderState oldState = state ;
2012-07-22 11:56:38 -02:30
state = state . transition ( ! errors . isEmpty ());
2013-06-11 14:41:19 -04:00
if ( state != desiredState && ! forceState )
2012-07-23 16:33:17 -02:30
{
2012-10-16 12:39:04 -04:00
Throwable toThrow = null ;
2012-07-23 16:33:17 -02:30
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 11:47:48 -05:00
FMLLog . severe ( sb . toString ());
2013-01-30 18:57:21 -05:00
if ( errors . size () > 0 )
2012-07-23 16:33:17 -02:30
{
2013-01-30 18:57:21 -05:00
FMLLog . severe ( "The following problems were captured during this phase" );
for ( Entry < String , Throwable > error : errors . entries ())
2012-10-16 12:39:04 -04:00
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , error . getValue (), "Caught exception from %s" , error . getKey ());
2013-01-30 18:57:21 -05:00
if ( error . getValue () instanceof IFMLHandledException )
{
toThrow = error . getValue ();
}
else if ( toThrow == null )
{
toThrow = error . getValue ();
}
2012-10-16 12:39:04 -04:00
}
}
2013-01-30 18:57:21 -05: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 12:39:04 -04:00
if ( toThrow != null && toThrow instanceof RuntimeException )
{
throw ( RuntimeException ) toThrow ;
}
else
{
throw new LoaderException ( toThrow );
2012-07-23 16:33:17 -02:30
}
}
2013-06-11 14:41:19 -04: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 11:56:38 -02:30
}
2012-07-30 00:33:21 -04:00
2012-07-23 22:50:37 -02:30
public ModContainer activeContainer ()
{
2014-01-25 06:17:14 -05:00
return activeContainer != null ? activeContainer : findActiveContainerFromStack ();
2012-07-23 22:50:37 -02:30
}
2012-08-11 02:43:04 -04:00
2012-07-14 13:58:46 -04:00
@Subscribe
2012-10-24 16:39:55 -04:00
public void propogateStateMessage ( FMLEvent stateEvent )
2012-07-22 11:56:38 -02:30
{
2012-08-11 16:21:03 -04:00
if ( stateEvent instanceof FMLPreInitializationEvent )
{
2012-08-15 20:05:12 -04:00
modObjectList = buildModObjectList ();
2012-08-11 16:21:03 -04:00
}
2012-08-24 12:10:43 -04:00
for ( ModContainer mc : activeModList )
2012-07-22 11:56:38 -02:30
{
2013-06-04 12:52:30 -04: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 11:47:48 -05: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 12:52:30 -04:00
modStates . put ( modId , ModState . ERRORED );
return ;
}
}
activeContainer = mc ;
stateEvent . applyModContainer ( activeContainer ());
2014-01-20 21:58:39 -05:00
ThreadContext . put ( "mod" , modId );
2013-12-16 11:47:48 -05:00
FMLLog . log ( modId , Level . TRACE , "Sending event %s to mod %s" , stateEvent . getEventType (), modId );
2013-06-04 12:52:30 -04:00
eventChannels . get ( modId ). post ( stateEvent );
2013-12-16 11:47:48 -05:00
FMLLog . log ( modId , Level . TRACE , "Sent event %s to mod %s" , stateEvent . getEventType (), modId );
2014-01-20 22:00:24 -05:00
ThreadContext . remove ( "mod" );
2013-06-04 12:52:30 -04:00
activeContainer = null ;
if ( stateEvent instanceof FMLStateEvent )
{
if ( ! errors . containsKey ( modId ))
{
modStates . put ( modId , (( FMLStateEvent ) stateEvent ). getModState ());
}
else
2012-07-22 11:56:38 -02:30
{
2013-06-04 12:52:30 -04:00
modStates . put ( modId , ModState . ERRORED );
2012-07-22 11:56:38 -02:30
}
}
}
2012-08-15 20:05:12 -04:00
public ImmutableBiMap < ModContainer , Object > buildModObjectList ()
2012-08-11 02:43:04 -04:00
{
2012-08-11 11:44:55 -04:00
ImmutableBiMap . Builder < ModContainer , Object > builder = ImmutableBiMap . < ModContainer , Object > builder ();
2012-08-11 02:43:04 -04:00
for ( ModContainer mc : activeModList )
{
2012-08-15 20:05:12 -04:00
if ( ! mc . isImmutable () && mc . getMod () != null )
2012-08-11 16:21:03 -04:00
{
builder . put ( mc , mc . getMod ());
2014-01-25 06:17:14 -05:00
List < String > packages = mc . getOwnedPackages ();
for ( String pkg : packages )
{
packageOwners . put ( pkg , mc );
}
2012-08-11 16:21:03 -04:00
}
2012-08-16 13:55:42 -04:00
if ( mc . getMod () == null && ! mc . isImmutable () && state != LoaderState . CONSTRUCTING )
2012-08-15 20:05:12 -04: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 02:43:04 -04:00
}
2012-08-15 20:05:12 -04:00
return builder . build ();
2012-08-11 02:43:04 -04:00
}
2012-08-11 11:44:55 -04:00
2012-07-22 11:56:38 -02:30
public void errorOccurred ( ModContainer modContainer , Throwable exception )
2012-07-14 13:58:46 -04:00
{
2012-10-16 12:39:04 -04:00
if ( exception instanceof InvocationTargetException )
{
errors . put ( modContainer . getModId (), (( InvocationTargetException ) exception ). getCause ());
}
else
{
errors . put ( modContainer . getModId (), exception );
}
2012-07-22 11:56:38 -02:30
}
public void printModStates ( StringBuilder ret )
{
2012-08-24 12:10:43 -04:00
for ( ModContainer mc : loader . getModList ())
2012-07-22 11:56:38 -02:30
{
2013-04-16 13:30:39 -04: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 12:10:43 -04:00
Joiner . on ( "->" ). appendTo ( ret , modStates . get ( mc . getModId ()));
2012-07-22 11:56:38 -02:30
}
2012-07-14 13:58:46 -04:00
}
2012-07-23 22:50:37 -02:30
public List < ModContainer > getActiveModList ()
{
return activeModList ;
}
public ModState getModState ( ModContainer selectedMod )
{
return Iterables . getLast ( modStates . get ( selectedMod . getModId ()), ModState . AVAILABLE );
}
2012-08-04 11:26:51 -04:00
public void distributeStateMessage ( Class <?> customEvent )
{
try
{
masterChannel . post ( customEvent . newInstance ());
}
catch ( Exception e )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , e , "An unexpected exception" );
2012-08-04 11:26:51 -04:00
throw new LoaderException ( e );
}
}
2012-08-11 02:43:04 -04:00
public BiMap < ModContainer , Object > getModObjectList ()
{
2012-08-15 20:05:12 -04: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 02:43:04 -04:00
return ImmutableBiMap . copyOf ( modObjectList );
}
2012-08-13 15:26:29 -04:00
public boolean isInState ( LoaderState state )
{
return this . state == state ;
}
2012-10-24 16:39:55 -04:00
boolean hasReachedState ( LoaderState state ) {
return this . state . ordinal () >= state . ordinal () && this . state != LoaderState . ERRORED ;
}
2013-03-07 19:37:52 -05:00
void forceState ( LoaderState newState )
{
this . state = newState ;
}
2014-01-25 06:17:14 -05:00
private ModContainer findActiveContainerFromStack ()
{
for ( Class <?> c : getCallingStack ())
{
int idx = c . getName (). lastIndexOf ( '.' );
if ( idx == - 1 )
{
continue ;
}
String pkg = c . getName (). substring ( 0 , idx );
if ( packageOwners . containsKey ( pkg ))
{
return packageOwners . get ( pkg ). get ( 0 );
}
}
return null ;
}
private FMLSecurityManager accessibleManager = new FMLSecurityManager ();
class FMLSecurityManager extends SecurityManager
{
Class <?>[] getStackClasses ()
{
return getClassContext ();
}
}
Class <?>[] getCallingStack ()
{
return accessibleManager . getStackClasses ();
}
2012-07-14 13:58:46 -04:00
}