2013-03-08 00:25:48 +00:00
/ *
2016-06-23 03:49:47 +00:00
* Minecraft Forge
2018-07-01 21:17:28 +00:00
* Copyright ( c ) 2016 - 2018 .
2013-03-08 00:37:52 +00:00
*
2016-06-23 03:49:47 +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 version 2 . 1
* of the License .
*
* 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
2013-03-08 00:25:48 +00:00
* /
2014-09-23 05:01:24 +00:00
package net.minecraftforge.fml.common ;
2012-07-14 17:58:46 +00:00
2012-10-16 16:39:04 +00:00
import java.lang.reflect.InvocationTargetException ;
2018-05-13 05:39:42 +00:00
import java.lang.reflect.Method ;
2013-06-04 16:52:30 +00:00
import java.util.Collection ;
2013-12-17 15:51:00 +00:00
import java.util.HashMap ;
2017-06-25 01:08:20 +00:00
import java.util.HashSet ;
2012-07-24 01:20:37 +00:00
import java.util.List ;
2017-06-25 01:08:20 +00:00
import java.util.stream.Collectors ;
2018-05-13 05:39:42 +00:00
import java.util.stream.Stream ;
2013-12-16 16:47:48 +00:00
2017-08-18 22:28:58 +00:00
import net.minecraftforge.common.util.TextTable ;
2015-05-11 03:55:11 +00:00
import net.minecraftforge.fml.common.ProgressManager.ProgressBar ;
2018-06-15 19:03:35 +00:00
import net.minecraftforge.fml.common.event.ModLifecycleEvent ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.event.FMLLoadEvent ;
import net.minecraftforge.fml.common.event.FMLModDisabledEvent ;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent ;
import net.minecraftforge.fml.common.versioning.ArtifactVersion ;
2018-06-15 19:03:35 +00:00
import net.minecraftforge.fml.ModContainer ;
2018-06-11 13:56:44 +00:00
import org.apache.logging.log4j.LogManager ;
2014-01-21 02:58:39 +00:00
import org.apache.logging.log4j.ThreadContext ;
2018-05-13 05:39:42 +00:00
import org.apache.logging.log4j.message.FormattedMessage ;
2012-07-22 14:26:38 +00:00
2018-05-13 05:39:42 +00:00
import com.google.common.base.Throwables ;
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 ;
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 ;
2014-01-25 11:17:14 +00:00
import com.google.common.collect.ListMultimap ;
2012-07-24 01:20:37 +00:00
import com.google.common.collect.Lists ;
2013-12-17 15:51:00 +00:00
import com.google.common.collect.Maps ;
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 ;
2017-01-11 23:17:56 +00:00
import javax.annotation.Nullable ;
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 ;
2018-05-13 05:39:42 +00:00
private ImmutableMap < String , EventBus > eventChannels ;
2012-07-22 14:26:38 +00:00
private LoaderState state ;
2012-07-23 19:03:17 +00:00
private Multimap < String , ModState > modStates = 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 ;
2014-01-25 11:17:14 +00:00
private ListMultimap < String , ModContainer > packageOwners ;
2012-07-30 04:33:21 +00:00
2012-07-22 14:26:38 +00:00
public LoadController ( Loader loader )
{
this . loader = loader ;
2018-05-13 05:39:42 +00:00
this . masterChannel = new FMLThrowingEventBus ( ( exception , context ) - > {
Throwables . throwIfUnchecked ( exception ) ;
// should not happen, but add some extra context for checked exceptions
Method method = context . getSubscriberMethod ( ) ;
String parameterNames = Stream . of ( method . getParameterTypes ( ) ) . map ( Class : : getName ) . collect ( Collectors . joining ( " , " ) ) ;
String message = " Exception thrown during LoadController. " + method . getName ( ) + '(' + parameterNames + ')' ;
throw new LoaderExceptionModCrash ( message , exception ) ;
2016-04-06 09:11:27 +00:00
} ) ;
2012-07-22 14:26:38 +00:00
this . masterChannel . register ( this ) ;
2012-07-30 04:33:21 +00:00
2012-08-04 15:26:51 +00:00
state = LoaderState . NOINIT ;
2014-01-25 11:17:14 +00:00
packageOwners = ArrayListMultimap . create ( ) ;
2012-08-11 15:44:55 +00:00
2012-08-04 15:26:51 +00:00
}
2013-12-17 15:51:00 +00: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 ( ) ) ;
2018-05-13 05:39:42 +00:00
eventChannels = ImmutableMap . copyOf ( temporary ) ;
modStates . put ( modId , ModState . DISABLED ) ;
modObjectList . remove ( mod ) ;
activeModList . remove ( mod ) ;
2013-12-17 15:51:00 +00:00
}
2018-05-13 05:39:42 +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
2016-03-20 00:07:04 +00:00
for ( final ModContainer mod : loader . getModList ( ) )
2012-07-22 14:26:38 +00:00
{
2018-05-13 05:39:42 +00:00
EventBus bus = new FMLThrowingEventBus ( ( exception , context ) - > this . errorOccurred ( mod , exception ) ) ;
2016-03-20 00:07:04 +00:00
2012-07-22 14:26:38 +00:00
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
{
2018-03-18 01:41:16 +00:00
FMLLog . log . warn ( " Mod {} 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 ( ) ;
}
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
{
2017-05-02 00:14:55 +00:00
if ( FMLCommonHandler . instance ( ) . isDisplayCloseRequested ( ) )
{
2017-06-23 05:33:11 +00:00
FMLLog . log . info ( " The game window is being closed by the player, exiting. " ) ;
2017-05-02 00:14:55 +00:00
FMLCommonHandler . instance ( ) . exitJava ( 0 , false ) ;
}
2012-07-23 19:03:17 +00:00
LoaderState oldState = state ;
2018-05-13 05:39:42 +00:00
state = state . transition ( false ) ;
2018-01-16 05:17:00 +00:00
if ( state ! = desiredState )
2012-07-23 19:03:17 +00:00
{
2018-01-16 05:17:00 +00:00
if ( ! forceState )
2012-07-23 19:03:17 +00:00
{
2018-05-13 05:39:42 +00:00
FormattedMessage message = new FormattedMessage ( " A fatal error occurred during the state transition from {} to {}. State became {} instead. Loading cannot continue. " , oldState , desiredState , state ) ;
throw new LoaderException ( message . getFormattedMessage ( ) ) ;
2012-10-16 16:39:04 +00:00
}
2013-01-30 23:57:21 +00:00
else
{
2018-01-16 05:17:00 +00:00
FMLLog . log . info ( " The state engine was in incorrect state {} and forced into state {}. Errors may have been discarded. " , state , desiredState ) ;
forceState ( desiredState ) ;
2013-01-30 23:57:21 +00:00
}
2018-01-16 05:17:00 +00:00
}
}
2017-01-11 23:17:56 +00:00
@Nullable
2012-07-24 01:20:37 +00:00
public ModContainer activeContainer ( )
{
2014-01-25 11:17:14 +00:00
return activeContainer ! = null ? activeContainer : findActiveContainerFromStack ( ) ;
2012-07-24 01:20:37 +00:00
}
2012-08-11 06:43:04 +00:00
2017-01-11 23:17:56 +00:00
void forceActiveContainer ( @Nullable ModContainer container )
2016-07-20 20:03:56 +00:00
{
activeContainer = container ;
}
2018-05-13 05:39:42 +00:00
2012-07-14 17:58:46 +00:00
@Subscribe
2018-06-15 19:03:35 +00:00
public void propogateStateMessage ( ModLifecycleEvent 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
}
2015-06-19 05:18:27 +00:00
ProgressBar bar = ProgressManager . push ( stateEvent . description ( ) , activeModList . size ( ) , true ) ;
2012-08-24 16:10:43 +00:00
for ( ModContainer mc : activeModList )
2012-07-22 14:26:38 +00:00
{
2015-04-24 20:00:22 +00:00
bar . step ( mc . getName ( ) ) ;
2013-06-04 16:52:30 +00:00
sendEventToModContainer ( stateEvent , mc ) ;
}
2015-04-21 03:34:36 +00:00
ProgressManager . pop ( bar ) ;
2013-06-04 16:52:30 +00:00
}
2018-06-15 19:03:35 +00:00
private void sendEventToModContainer ( ModLifecycleEvent stateEvent , ModContainer mc )
2013-06-04 16:52:30 +00:00
{
String modId = mc . getModId ( ) ;
2018-05-13 05:39:42 +00:00
Collection < String > requirements = mc . getRequirements ( ) . stream ( ) . map ( ArtifactVersion : : getLabel ) . collect ( Collectors . toCollection ( HashSet : : new ) ) ;
2013-06-04 16:52:30 +00:00
for ( ArtifactVersion av : mc . getDependencies ( ) )
{
2018-05-13 05:39:42 +00:00
if ( av . getLabel ( ) ! = null & & requirements . contains ( av . getLabel ( ) ) & & modStates . containsEntry ( av . getLabel ( ) , ModState . ERRORED ) )
2013-06-04 16:52:30 +00:00
{
2018-06-11 13:56:44 +00:00
LogManager . getLogger ( modId ) . error ( " Skipping event {} and marking errored mod {} since required dependency {} has errored " , stateEvent . getEventType ( ) , modId , av . getLabel ( ) ) ;
2013-06-04 16:52:30 +00:00
modStates . put ( modId , ModState . ERRORED ) ;
return ;
}
}
activeContainer = mc ;
2017-01-11 23:17:56 +00:00
stateEvent . applyModContainer ( mc ) ;
2014-01-21 02:58:39 +00:00
ThreadContext . put ( " mod " , modId ) ;
2018-06-11 13:56:44 +00:00
LogManager . getLogger ( modId ) . trace ( " Sending event {} to mod {} " , stateEvent . getEventType ( ) , modId ) ;
2013-06-04 16:52:30 +00:00
eventChannels . get ( modId ) . post ( stateEvent ) ;
2018-06-11 13:56:44 +00:00
LogManager . getLogger ( modId ) . trace ( " Sent event {} to mod {} " , stateEvent . getEventType ( ) , modId ) ;
2014-01-21 03:00:24 +00:00
ThreadContext . remove ( " mod " ) ;
2013-06-04 16:52:30 +00:00
activeContainer = null ;
if ( stateEvent instanceof FMLStateEvent )
{
2018-06-11 13:56:44 +00:00
if ( ! errors . containsKey ( modId ) )
{
modStates . put ( modId , ( ( FMLStateEvent ) stateEvent ) . getModState ( ) ) ;
}
else
{
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
{
2016-03-23 14:34:48 +00:00
ImmutableBiMap . Builder < ModContainer , Object > builder = ImmutableBiMap . builder ( ) ;
2012-08-11 06:43:04 +00:00
for ( ModContainer mc : activeModList )
{
2018-05-13 05:39:42 +00:00
if ( ! mc . isImmutable ( ) & & mc . getMod ( ) ! = null )
2012-08-11 20:21:03 +00:00
{
builder . put ( mc , mc . getMod ( ) ) ;
2014-01-25 11:17:14 +00:00
List < String > packages = mc . getOwnedPackages ( ) ;
for ( String pkg : packages )
{
packageOwners . put ( pkg , mc ) ;
}
2012-08-11 20:21:03 +00:00
}
2018-05-13 05:39:42 +00:00
if ( mc . getMod ( ) = = null & & ! mc . isImmutable ( ) & & state ! = LoaderState . CONSTRUCTING )
2012-08-16 00:05:12 +00:00
{
2018-05-13 05:39:42 +00:00
FormattedMessage message = new FormattedMessage ( " There is a severe problem with {} ({}) - it appears not to have constructed correctly " , mc . getName ( ) , mc . getModId ( ) ) ;
this . errorOccurred ( mc , new RuntimeException ( message . getFormattedMessage ( ) ) ) ;
2012-08-16 00:05:12 +00:00
}
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
{
2018-05-13 05:39:42 +00:00
String modId = modContainer . getModId ( ) ;
String modName = modContainer . getName ( ) ;
modStates . put ( modId , ModState . ERRORED ) ;
2012-10-16 16:39:04 +00:00
if ( exception instanceof InvocationTargetException )
{
2018-05-13 05:39:42 +00:00
exception = exception . getCause ( ) ;
2012-10-16 16:39:04 +00:00
}
2018-05-13 05:39:42 +00:00
if ( exception instanceof LoaderException ) // avoid wrapping loader exceptions multiple times
2012-10-16 16:39:04 +00:00
{
2018-05-13 05:39:42 +00:00
throw ( LoaderException ) exception ;
2012-10-16 16:39:04 +00:00
}
2018-05-13 05:39:42 +00:00
FormattedMessage message = new FormattedMessage ( " Caught exception from {} ({}) " , modName , modId ) ;
throw new LoaderExceptionModCrash ( message . getFormattedMessage ( ) , exception ) ;
2012-07-22 14:26:38 +00:00
}
public void printModStates ( StringBuilder ret )
{
2015-06-10 06:06:06 +00:00
ret . append ( " \ n \ tStates: " ) ;
for ( ModState state : ModState . values ( ) )
ret . append ( " ' " ) . append ( state . getMarker ( ) ) . append ( " ' = " ) . append ( state . toString ( ) ) ;
2017-08-18 22:28:58 +00:00
TextTable table = new TextTable ( Lists . newArrayList (
TextTable . column ( " State " ) ,
TextTable . column ( " ID " ) ,
TextTable . column ( " Version " ) ,
TextTable . column ( " Source " ) ,
TextTable . column ( " Signature " ) )
) ;
2012-08-24 16:10:43 +00:00
for ( ModContainer mc : loader . getModList ( ) )
2012-07-22 14:26:38 +00:00
{
2017-08-18 22:28:58 +00:00
table . add (
modStates . get ( mc . getModId ( ) ) . stream ( ) . map ( ModState : : getMarker ) . reduce ( " " , ( a , b ) - > a + b ) ,
mc . getModId ( ) ,
mc . getVersion ( ) ,
mc . getSource ( ) . getName ( ) ,
mc . getSigningCertificate ( ) ! = null ? CertificateHelper . getFingerprint ( mc . getSigningCertificate ( ) ) : " None "
) ;
2012-07-22 14:26:38 +00:00
}
2017-08-18 22:28:58 +00:00
ret . append ( " \ n " ) ;
ret . append ( " \ n \ t " ) ;
table . append ( ret , " \ n \ t " ) ;
ret . append ( " \ n " ) ;
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 )
{
2018-05-13 05:39:42 +00:00
Object eventInstance ;
2012-08-04 15:26:51 +00:00
try
{
2018-05-13 05:39:42 +00:00
eventInstance = customEvent . newInstance ( ) ;
2012-08-04 15:26:51 +00:00
}
2018-05-13 05:39:42 +00:00
catch ( InstantiationException | IllegalAccessException e )
2012-08-04 15:26:51 +00:00
{
2018-05-13 05:39:42 +00:00
throw new LoaderException ( " Failed to create new event instance for " + customEvent . getName ( ) , e ) ;
2012-08-04 15:26:51 +00:00
}
2018-05-13 05:39:42 +00:00
masterChannel . post ( eventInstance ) ;
2012-08-04 15:26:51 +00:00
}
2012-08-11 06:43:04 +00:00
public BiMap < ModContainer , Object > getModObjectList ( )
{
2012-08-16 00:05:12 +00:00
if ( modObjectList = = null )
{
2017-06-23 05:33:11 +00:00
FMLLog . log . fatal ( " Detected an attempt by a mod {} to perform game activity during mod construction. This is a serious programming error. " , activeContainer ) ;
2012-08-16 00:05:12 +00:00
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
2018-05-13 05:39:42 +00:00
boolean hasReachedState ( LoaderState state )
{
return this . state . ordinal ( ) > = state . ordinal ( ) & & this . state ! = LoaderState . ERRORED ;
2016-04-06 09:11:27 +00:00
}
2013-03-08 00:37:52 +00:00
void forceState ( LoaderState newState )
{
this . state = newState ;
}
2014-01-25 11:17:14 +00:00
2017-01-11 23:17:56 +00:00
@Nullable
2014-01-25 11:17:14 +00:00
private ModContainer findActiveContainerFromStack ( )
{
for ( Class < ? > c : getCallingStack ( ) )
{
int idx = c . getName ( ) . lastIndexOf ( '.' ) ;
if ( idx = = - 1 )
{
continue ;
}
2018-05-13 05:39:42 +00:00
String pkg = c . getName ( ) . substring ( 0 , idx ) ;
2014-01-25 11:17:14 +00:00
if ( packageOwners . containsKey ( pkg ) )
{
return packageOwners . get ( pkg ) . get ( 0 ) ;
}
}
return null ;
}
2018-05-13 05:39:42 +00:00
2014-01-25 11:17:14 +00:00
private FMLSecurityManager accessibleManager = new FMLSecurityManager ( ) ;
class FMLSecurityManager extends SecurityManager
{
Class < ? > [ ] getStackClasses ( )
{
return getClassContext ( ) ;
}
}
Class < ? > [ ] getCallingStack ( )
{
return accessibleManager . getStackClasses ( ) ;
}
2015-05-07 18:17:45 +00:00
LoaderState getState ( )
{
return state ;
}
2012-07-14 17:58:46 +00:00
}