2012-03-30 14:11:13 +00:00
/ *
2016-06-23 03:49:47 +00:00
* Minecraft Forge
* Copyright ( c ) 2016 .
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
2012-03-30 14:11:13 +00:00
* /
2013-03-08 00:25:48 +00:00
2014-09-23 05:01:24 +00:00
package net.minecraftforge.fml.common ;
2012-03-28 20:44:36 +00:00
import java.io.File ;
2012-07-22 14:26:38 +00:00
import java.io.FileReader ;
2013-12-17 15:51:00 +00:00
import java.io.FileWriter ;
2012-03-28 20:44:36 +00:00
import java.io.IOException ;
2012-08-31 00:10:13 +00:00
import java.net.MalformedURLException ;
2014-03-24 20:16:36 +00:00
import java.util.ArrayList ;
2015-06-10 05:25:13 +00:00
import java.util.Collections ;
2012-08-14 17:36:29 +00:00
import java.util.Comparator ;
2013-12-09 06:22:24 +00:00
import java.util.Iterator ;
2012-03-28 20:44:36 +00:00
import java.util.List ;
2012-03-30 05:12:59 +00:00
import java.util.Map ;
2012-05-10 13:42:31 +00:00
import java.util.Properties ;
2012-08-23 17:43:25 +00:00
import java.util.Set ;
2014-03-24 20:16:36 +00:00
2015-11-28 08:01:31 +00:00
import net.minecraft.util.ResourceLocation ;
2016-01-12 00:04:22 +00:00
import net.minecraftforge.common.capabilities.CapabilityManager ;
2016-10-11 05:15:35 +00:00
import net.minecraftforge.common.config.ConfigManager ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.LoaderState.ModState ;
import net.minecraftforge.fml.common.ModContainer.Disableable ;
2015-05-11 03:55:11 +00:00
import net.minecraftforge.fml.common.ProgressManager.ProgressBar ;
2016-07-20 20:03:56 +00:00
import net.minecraftforge.fml.common.discovery.ASMDataTable ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.discovery.ModDiscoverer ;
import net.minecraftforge.fml.common.event.FMLInterModComms ;
import net.minecraftforge.fml.common.event.FMLLoadEvent ;
import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent ;
import net.minecraftforge.fml.common.event.FMLModIdMappingEvent ;
import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent.MissingMapping ;
import net.minecraftforge.fml.common.functions.ArtifactVersionNameFunction ;
import net.minecraftforge.fml.common.functions.ModIdFunction ;
2015-11-28 08:01:31 +00:00
import net.minecraftforge.fml.common.registry.* ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.registry.GameRegistry.Type ;
import net.minecraftforge.fml.common.toposort.ModSorter ;
import net.minecraftforge.fml.common.toposort.ModSortingException ;
import net.minecraftforge.fml.common.toposort.TopologicalSort ;
import net.minecraftforge.fml.common.toposort.ModSortingException.SortingExceptionData ;
import net.minecraftforge.fml.common.versioning.ArtifactVersion ;
import net.minecraftforge.fml.common.versioning.VersionParser ;
2015-05-11 03:55:11 +00:00
import net.minecraftforge.fml.relauncher.ModListHelper ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.relauncher.Side ;
2013-12-16 16:47:48 +00:00
import org.apache.logging.log4j.Level ;
2014-03-24 20:16:36 +00:00
2012-07-22 14:26:38 +00:00
import com.google.common.base.CharMatcher ;
import com.google.common.base.Function ;
import com.google.common.base.Joiner ;
import com.google.common.base.Splitter ;
2013-12-17 15:51:00 +00:00
import com.google.common.collect.ArrayListMultimap ;
2012-08-11 06:43:04 +00:00
import com.google.common.collect.BiMap ;
2012-08-23 17:43:25 +00:00
import com.google.common.collect.HashBiMap ;
2012-07-22 14:26:38 +00:00
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableMap ;
2012-08-14 17:36:29 +00:00
import com.google.common.collect.ImmutableMultiset ;
2014-06-27 02:09:50 +00:00
import com.google.common.collect.Iterables ;
2012-10-24 13:41:46 +00:00
import com.google.common.collect.LinkedHashMultimap ;
2013-12-17 15:51:00 +00:00
import com.google.common.collect.ListMultimap ;
2012-07-22 14:26:38 +00:00
import com.google.common.collect.Lists ;
import com.google.common.collect.Maps ;
2013-12-17 15:51:00 +00:00
import com.google.common.collect.Multimaps ;
2012-08-14 17:36:29 +00:00
import com.google.common.collect.Multiset.Entry ;
import com.google.common.collect.Multisets ;
import com.google.common.collect.Ordering ;
2013-11-10 16:49:06 +00:00
import com.google.common.collect.SetMultimap ;
import com.google.common.collect.Sets ;
2012-08-14 17:36:29 +00:00
import com.google.common.collect.TreeMultimap ;
2015-04-25 05:30:17 +00:00
import com.google.gson.JsonArray ;
import com.google.gson.JsonElement ;
import com.google.gson.JsonObject ;
import com.google.gson.JsonParser ;
2014-03-24 20:16:36 +00:00
2012-04-05 20:22:47 +00:00
/ * *
* The loader class performs the actual loading of the mod code from disk .
2012-07-30 02:54:59 +00:00
*
2012-07-22 14:26:38 +00:00
* < p >
* There are several { @link LoaderState } s to mod loading , triggered in two
* different stages from the FML handler code ' s hooks into the minecraft code .
* < / p >
2012-07-30 02:54:59 +00:00
*
2012-04-05 20:22:47 +00:00
* < ol >
2012-07-22 14:26:38 +00:00
* < li > LOADING . Scanning the filesystem for mod containers to load ( zips , jars ,
* directories ) , adding them to the { @link # modClassLoader } Scanning , the loaded
* containers for mod classes to load and registering them appropriately . < / li >
* < li > PREINIT . The mod classes are configured , they are sorted into a load
* order , and instances of the mods are constructed . < / li >
* < li > INIT . The mod instances are initialized . For BaseMod mods , this involves
* calling the load method . < / li >
* < li > POSTINIT . The mod instances are post initialized . For BaseMod mods this
* involves calling the modsLoaded method . < / li >
2012-04-05 20:22:47 +00:00
* < li > UP . The Loader is complete < / li >
2012-07-22 14:26:38 +00:00
* < li > ERRORED . The loader encountered an error during the LOADING phase and
* dropped to this state instead . It will not complete loading from this state ,
* but it attempts to continue loading before abandoning and giving a fatal
* error . < / li >
2012-04-05 20:22:47 +00:00
* < / ol >
2012-07-30 02:54:59 +00:00
*
2012-07-22 14:26:38 +00:00
* Phase 1 code triggers the LOADING and PREINIT states . Phase 2 code triggers
* the INIT and POSTINIT states .
2012-07-30 02:54:59 +00:00
*
2012-04-05 20:22:47 +00:00
* @author cpw
2012-07-30 02:54:59 +00:00
*
2012-04-05 20:22:47 +00:00
* /
2015-11-27 03:38:21 +00:00
@SuppressWarnings ( " unused " )
2012-04-05 14:07:52 +00:00
public class Loader
{
2016-03-09 22:52:32 +00:00
public static final String MC_VERSION = net . minecraftforge . common . ForgeVersion . mcVersion ;
2012-07-22 14:26:38 +00:00
private static final Splitter DEPENDENCYPARTSPLITTER = Splitter . on ( " : " ) . omitEmptyStrings ( ) . trimResults ( ) ;
private static final Splitter DEPENDENCYSPLITTER = Splitter . on ( " ; " ) . omitEmptyStrings ( ) . trimResults ( ) ;
2012-04-05 20:22:47 +00:00
/ * *
* The singleton instance
* /
2012-04-05 14:07:52 +00:00
private static Loader instance ;
2012-04-05 20:22:47 +00:00
/ * *
* Build information for tracking purposes .
* /
2012-05-10 13:42:31 +00:00
private static String major ;
private static String minor ;
private static String rev ;
private static String build ;
2012-07-02 16:24:37 +00:00
private static String mccversion ;
2012-10-25 13:02:49 +00:00
private static String mcpversion ;
2012-04-05 14:07:52 +00:00
2012-04-05 20:22:47 +00:00
/ * *
* The class loader we load the mods into .
* /
2012-04-05 14:07:52 +00:00
private ModClassLoader modClassLoader ;
2012-04-05 20:22:47 +00:00
/ * *
* The sorted list of mods .
* /
2012-04-05 14:07:52 +00:00
private List < ModContainer > mods ;
2012-04-05 20:22:47 +00:00
/ * *
* A named list of mods
* /
2012-04-05 14:07:52 +00:00
private Map < String , ModContainer > namedMods ;
2013-12-17 15:51:00 +00:00
/ * *
* A reverse dependency graph for mods
* /
private ListMultimap < String , String > reverseDependencies ;
2012-04-05 20:22:47 +00:00
/ * *
* The canonical configuration directory
* /
2012-04-05 14:07:52 +00:00
private File canonicalConfigDir ;
2012-07-22 14:26:38 +00:00
private File canonicalModsDir ;
private LoadController modController ;
2012-10-02 05:29:46 +00:00
private MinecraftDummyContainer minecraft ;
2013-08-28 17:18:28 +00:00
private MCPDummyContainer mcp ;
2012-06-26 20:24:50 +00:00
2012-07-31 02:31:07 +00:00
private static File minecraftDir ;
2012-08-04 15:26:51 +00:00
private static List < String > injectedContainers ;
2013-03-30 16:12:54 +00:00
private ImmutableMap < String , String > fmlBrandingProperties ;
2013-12-17 15:51:00 +00:00
private File forcedModFile ;
2014-05-08 14:22:02 +00:00
private ModDiscoverer discoverer ;
2015-04-21 03:34:36 +00:00
private ProgressBar progressBar ;
2016-05-04 01:26:52 +00:00
public final boolean java8 ;
2012-07-31 02:31:07 +00:00
2012-04-05 14:07:52 +00:00
public static Loader instance ( )
{
if ( instance = = null )
{
instance = new Loader ( ) ;
}
return instance ;
2012-04-03 16:03:21 +00:00
}
2012-04-05 20:22:47 +00:00
2013-11-10 16:49:06 +00:00
@SuppressWarnings ( " unchecked " )
2012-07-31 02:31:07 +00:00
public static void injectData ( Object . . . data )
2012-07-14 17:58:46 +00:00
{
2012-07-31 02:31:07 +00:00
major = ( String ) data [ 0 ] ;
minor = ( String ) data [ 1 ] ;
rev = ( String ) data [ 2 ] ;
build = ( String ) data [ 3 ] ;
mccversion = ( String ) data [ 4 ] ;
2012-10-25 13:02:49 +00:00
mcpversion = ( String ) data [ 5 ] ;
2012-07-31 02:31:07 +00:00
minecraftDir = ( File ) data [ 6 ] ;
2012-08-04 15:26:51 +00:00
injectedContainers = ( List < String > ) data [ 7 ] ;
2012-07-31 02:31:07 +00:00
}
2012-07-30 02:54:59 +00:00
2012-07-31 02:31:07 +00:00
private Loader ( )
{
2016-05-04 01:26:52 +00:00
String [ ] ver = System . getProperty ( " java.version " ) . split ( " \\ . " ) ;
int major = Integer . parseInt ( ver [ 1 ] ) ;
java8 = major > 7 ;
if ( ! java8 )
{
FMLLog . severe ( " The game is not running with Java 8. Forge recommends Java 8 for maximum compatibility with mods " ) ;
}
2012-07-14 17:58:46 +00:00
modClassLoader = new ModClassLoader ( getClass ( ) . getClassLoader ( ) ) ;
2016-07-19 23:21:52 +00:00
if ( mccversion ! = null & & ! mccversion . equals ( MC_VERSION ) )
2012-08-13 04:17:34 +00:00
{
2014-06-26 18:37:28 +00:00
FMLLog . severe ( " This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file " , mccversion , MC_VERSION ) ;
2015-03-27 08:17:57 +00:00
throw new LoaderException ( String . format ( " This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file " , mccversion , MC_VERSION ) ) ;
2012-08-13 04:17:34 +00:00
}
2012-10-02 05:29:46 +00:00
2014-06-26 18:37:28 +00:00
minecraft = new MinecraftDummyContainer ( MC_VERSION ) ;
2012-10-25 13:02:49 +00:00
mcp = new MCPDummyContainer ( MetadataCollection . from ( getClass ( ) . getResourceAsStream ( " /mcpmod.info " ) , " MCP " ) . getMetadataForId ( " mcp " , null ) ) ;
2012-07-14 17:58:46 +00:00
}
2012-04-05 20:22:47 +00:00
/ * *
2012-07-22 14:26:38 +00:00
* Sort the mods into a sorted list , using dependency information from the
* containers . The sorting is performed using a { @link TopologicalSort }
* based on the pre - and post - dependency information provided by the mods .
2012-04-05 20:22:47 +00:00
* /
2012-04-05 14:07:52 +00:00
private void sortModList ( )
{
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Verifying mod requirements are satisfied " ) ;
2012-07-14 17:58:46 +00:00
try
2012-04-05 14:07:52 +00:00
{
2012-08-23 17:43:25 +00:00
BiMap < String , ArtifactVersion > modVersions = HashBiMap . create ( ) ;
2014-06-27 02:09:50 +00:00
for ( ModContainer mod : Iterables . concat ( getActiveModList ( ) , ModAPIManager . INSTANCE . getAPIList ( ) ) )
2012-04-05 14:07:52 +00:00
{
2012-07-30 21:01:27 +00:00
modVersions . put ( mod . getModId ( ) , mod . getProcessedVersion ( ) ) ;
}
2013-12-17 15:51:00 +00:00
ArrayListMultimap < String , String > reqList = ArrayListMultimap . create ( ) ;
2012-08-23 18:33:54 +00:00
for ( ModContainer mod : getActiveModList ( ) )
2012-07-30 21:01:27 +00:00
{
2012-10-02 05:29:46 +00:00
if ( ! mod . acceptableMinecraftVersionRange ( ) . containsVersion ( minecraft . getProcessedVersion ( ) ) )
{
FMLLog . severe ( " The mod %s does not wish to run in Minecraft version %s. You will have to remove it to play. " , mod . getModId ( ) , getMCVersionString ( ) ) ;
2016-01-10 19:07:12 +00:00
RuntimeException ret = new WrongMinecraftVersionException ( mod , getMCVersionString ( ) ) ;
FMLLog . severe ( ret . getMessage ( ) ) ;
throw ret ;
2012-10-02 05:29:46 +00:00
}
2013-06-04 16:52:30 +00:00
Map < String , ArtifactVersion > names = Maps . uniqueIndex ( mod . getRequirements ( ) , new ArtifactVersionNameFunction ( ) ) ;
2012-08-23 19:29:17 +00:00
Set < ArtifactVersion > versionMissingMods = Sets . newHashSet ( ) ;
2013-12-17 15:51:00 +00:00
2012-10-02 05:29:46 +00:00
Set < String > missingMods = Sets . difference ( names . keySet ( ) , modVersions . keySet ( ) ) ;
2012-08-23 17:43:25 +00:00
if ( ! missingMods . isEmpty ( ) )
{
FMLLog . severe ( " The mod %s (%s) requires mods %s to be available " , mod . getModId ( ) , mod . getName ( ) , missingMods ) ;
2012-08-23 19:29:17 +00:00
for ( String modid : missingMods )
{
versionMissingMods . add ( names . get ( modid ) ) ;
}
2016-01-10 19:07:12 +00:00
RuntimeException ret = new MissingModsException ( versionMissingMods , mod . getModId ( ) , mod . getName ( ) ) ;
FMLLog . severe ( ret . getMessage ( ) ) ;
throw ret ;
2012-08-23 17:43:25 +00:00
}
2013-12-17 15:51:00 +00:00
reqList . putAll ( mod . getModId ( ) , names . keySet ( ) ) ;
2012-07-30 21:01:27 +00:00
ImmutableList < ArtifactVersion > allDeps = ImmutableList . < ArtifactVersion > builder ( ) . addAll ( mod . getDependants ( ) ) . addAll ( mod . getDependencies ( ) ) . build ( ) ;
for ( ArtifactVersion v : allDeps )
2012-07-14 17:58:46 +00:00
{
2012-07-30 21:01:27 +00:00
if ( modVersions . containsKey ( v . getLabel ( ) ) )
{
if ( ! v . containsVersion ( modVersions . get ( v . getLabel ( ) ) ) )
{
2012-08-23 19:29:17 +00:00
versionMissingMods . add ( v ) ;
2012-07-30 21:01:27 +00:00
}
}
2012-07-14 17:58:46 +00:00
}
2012-08-23 19:29:17 +00:00
if ( ! versionMissingMods . isEmpty ( ) )
2012-08-23 17:43:25 +00:00
{
2012-09-07 23:08:28 +00:00
FMLLog . severe ( " The mod %s (%s) requires mod versions %s to be available " , mod . getModId ( ) , mod . getName ( ) , versionMissingMods ) ;
2016-01-10 19:07:12 +00:00
RuntimeException ret = new MissingModsException ( versionMissingMods , mod . getModId ( ) , mod . getName ( ) ) ;
FMLLog . severe ( ret . toString ( ) ) ;
throw ret ;
2012-08-23 17:43:25 +00:00
}
2012-04-05 14:07:52 +00:00
}
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " All mod requirements are satisfied " ) ;
2012-07-30 02:54:59 +00:00
2013-12-17 15:51:00 +00:00
reverseDependencies = Multimaps . invertFrom ( reqList , ArrayListMultimap . < String , String > create ( ) ) ;
2012-08-23 18:33:54 +00:00
ModSorter sorter = new ModSorter ( getActiveModList ( ) , namedMods ) ;
2012-04-05 14:07:52 +00:00
2012-07-14 17:58:46 +00:00
try
{
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Sorting mods into an ordered list " ) ;
2012-08-23 19:29:17 +00:00
List < ModContainer > sortedMods = sorter . sort ( ) ;
2012-08-24 15:58:18 +00:00
// Reset active list to the sorted list
modController . getActiveModList ( ) . clear ( ) ;
modController . getActiveModList ( ) . addAll ( sortedMods ) ;
// And inject the sorted list into the overall list
2012-08-23 19:29:17 +00:00
mods . removeAll ( sortedMods ) ;
sortedMods . addAll ( mods ) ;
mods = sortedMods ;
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Mod sorting completed successfully " ) ;
2012-07-14 17:58:46 +00:00
}
catch ( ModSortingException sortException )
{
2012-07-30 02:54:59 +00:00
FMLLog . severe ( " A dependency cycle was detected in the input mod set so an ordering cannot be determined " ) ;
2013-05-27 15:03:23 +00:00
SortingExceptionData < ModContainer > exceptionData = sortException . getExceptionData ( ) ;
FMLLog . severe ( " The first mod in the cycle is %s " , exceptionData . getFirstBadNode ( ) ) ;
FMLLog . severe ( " The mod cycle involves " ) ;
for ( ModContainer mc : exceptionData . getVisitedNodes ( ) )
{
FMLLog . severe ( " %s : before: %s, after: %s " , mc . toString ( ) , mc . getDependants ( ) , mc . getDependencies ( ) ) ;
}
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , sortException , " The full error " ) ;
2013-05-27 14:46:22 +00:00
throw sortException ;
2012-07-14 17:58:46 +00:00
}
}
finally
2012-04-05 14:07:52 +00:00
{
2013-01-30 23:57:21 +00:00
FMLLog . fine ( " Mod sorting data " ) ;
int unprintedMods = mods . size ( ) ;
2012-08-23 18:33:54 +00:00
for ( ModContainer mod : getActiveModList ( ) )
2012-05-10 08:00:07 +00:00
{
2012-08-04 15:26:51 +00:00
if ( ! mod . isImmutable ( ) )
{
2012-08-23 18:28:49 +00:00
FMLLog . fine ( " \ t%s(%s:%s): %s (%s) " , mod . getModId ( ) , mod . getName ( ) , mod . getVersion ( ) , mod . getSource ( ) . getName ( ) , mod . getSortingRules ( ) ) ;
2013-01-30 23:57:21 +00:00
unprintedMods - - ;
2012-08-04 15:26:51 +00:00
}
2012-07-23 19:03:17 +00:00
}
2013-01-30 23:57:21 +00:00
if ( unprintedMods = = mods . size ( ) )
2012-07-30 02:54:59 +00:00
{
2013-01-30 23:57:21 +00:00
FMLLog . fine ( " No user mods found to sort " ) ;
2012-05-10 08:00:07 +00:00
}
2012-04-05 14:07:52 +00:00
}
2012-07-14 17:58:46 +00:00
2012-03-28 20:44:36 +00:00
}
2012-04-05 14:07:52 +00:00
2012-04-05 20:22:47 +00:00
/ * *
2012-07-22 14:26:38 +00:00
* The primary loading code
2012-07-30 02:54:59 +00:00
*
*
2012-07-22 14:26:38 +00:00
* The found resources are first loaded into the { @link # modClassLoader }
* ( always ) then scanned for class resources matching the specification
* above .
2012-07-30 02:54:59 +00:00
*
2012-07-22 14:26:38 +00:00
* If they provide the { @link Mod } annotation , they will be loaded as
2013-11-10 18:15:26 +00:00
* " FML mods "
2012-07-30 02:54:59 +00:00
*
2012-07-22 14:26:38 +00:00
* Finally , if they are successfully loaded as classes , they are then added
* to the available mod list .
2012-04-05 20:22:47 +00:00
* /
2016-06-13 13:33:26 +00:00
private ModDiscoverer identifyMods ( List < String > additionalContainers )
2012-04-05 14:07:52 +00:00
{
2016-06-13 13:33:26 +00:00
injectedContainers . addAll ( additionalContainers ) ;
2012-08-04 15:26:51 +00:00
FMLLog . fine ( " Building injected Mod Containers %s " , injectedContainers ) ;
2012-10-25 13:02:49 +00:00
// Add in the MCP mod container
2012-10-26 11:55:23 +00:00
mods . add ( new InjectedModContainer ( mcp , new File ( " minecraft.jar " ) ) ) ;
2012-08-04 15:26:51 +00:00
for ( String cont : injectedContainers )
{
ModContainer mc ;
try
{
mc = ( ModContainer ) Class . forName ( cont , true , modClassLoader ) . newInstance ( ) ;
}
catch ( Exception e )
{
2016-03-23 14:34:48 +00:00
FMLLog . log ( Level . ERROR , e , " A problem occurred instantiating the injected mod container %s " , cont ) ;
2012-08-04 15:26:51 +00:00
throw new LoaderException ( e ) ;
}
2013-08-27 16:31:37 +00:00
mods . add ( new InjectedModContainer ( mc , mc . getSource ( ) ) ) ;
2012-08-04 15:26:51 +00:00
}
2012-07-22 14:26:38 +00:00
ModDiscoverer discoverer = new ModDiscoverer ( ) ;
2012-07-30 02:54:59 +00:00
FMLLog . fine ( " Attempting to load mods contained in the minecraft jar file and associated classes " ) ;
2012-07-22 14:26:38 +00:00
discoverer . findClasspathMods ( modClassLoader ) ;
2012-07-30 02:54:59 +00:00
FMLLog . fine ( " Minecraft jar mods loaded successfully " ) ;
2012-04-05 14:07:52 +00:00
2015-04-22 12:31:25 +00:00
FMLLog . getLogger ( ) . log ( Level . INFO , " Found {} mods from the command line. Injecting into mod discoverer " , ModListHelper . additionalMods . size ( ) ) ;
2012-07-30 02:54:59 +00:00
FMLLog . info ( " Searching %s for mods " , canonicalModsDir . getAbsolutePath ( ) ) ;
2015-04-22 12:31:25 +00:00
discoverer . findModDirMods ( canonicalModsDir , ModListHelper . additionalMods . values ( ) . toArray ( new File [ 0 ] ) ) ;
2013-07-03 03:03:01 +00:00
File versionSpecificModsDir = new File ( canonicalModsDir , mccversion ) ;
if ( versionSpecificModsDir . isDirectory ( ) )
{
FMLLog . info ( " Also searching %s for mods " , versionSpecificModsDir ) ;
discoverer . findModDirMods ( versionSpecificModsDir ) ;
}
2012-04-05 14:07:52 +00:00
2012-08-04 15:26:51 +00:00
mods . addAll ( discoverer . identifyMods ( ) ) ;
2012-08-14 17:36:29 +00:00
identifyDuplicates ( mods ) ;
2012-07-22 14:26:38 +00:00
namedMods = Maps . uniqueIndex ( mods , new ModIdFunction ( ) ) ;
2012-07-30 02:54:59 +00:00
FMLLog . info ( " Forge Mod Loader has identified %d mod%s to load " , mods . size ( ) , mods . size ( ) ! = 1 ? " s " : " " ) ;
2012-08-06 13:52:42 +00:00
return discoverer ;
2012-03-28 20:44:36 +00:00
}
2012-04-05 14:07:52 +00:00
2012-08-14 17:36:29 +00:00
private class ModIdComparator implements Comparator < ModContainer >
{
@Override
public int compare ( ModContainer o1 , ModContainer o2 )
{
return o1 . getModId ( ) . compareTo ( o2 . getModId ( ) ) ;
}
}
private void identifyDuplicates ( List < ModContainer > mods )
{
TreeMultimap < ModContainer , File > dupsearch = TreeMultimap . create ( new ModIdComparator ( ) , Ordering . arbitrary ( ) ) ;
for ( ModContainer mc : mods )
{
if ( mc . getSource ( ) ! = null )
{
dupsearch . put ( mc , mc . getSource ( ) ) ;
}
}
ImmutableMultiset < ModContainer > duplist = Multisets . copyHighestCountFirst ( dupsearch . keys ( ) ) ;
2012-10-24 13:41:46 +00:00
SetMultimap < ModContainer , File > dupes = LinkedHashMultimap . create ( ) ;
2012-08-14 17:36:29 +00:00
for ( Entry < ModContainer > e : duplist . entrySet ( ) )
{
if ( e . getCount ( ) > 1 )
{
FMLLog . severe ( " Found a duplicate mod %s at %s " , e . getElement ( ) . getModId ( ) , dupsearch . get ( e . getElement ( ) ) ) ;
2012-10-24 13:41:46 +00:00
dupes . putAll ( e . getElement ( ) , dupsearch . get ( e . getElement ( ) ) ) ;
2012-08-14 17:36:29 +00:00
}
}
2012-10-24 13:41:46 +00:00
if ( ! dupes . isEmpty ( ) )
{
2013-08-28 17:18:28 +00:00
throw new DuplicateModsFoundException ( dupes ) ;
2012-10-24 13:41:46 +00:00
}
2012-08-14 17:36:29 +00:00
}
2012-04-05 20:22:47 +00:00
/ * *
2013-01-22 02:37:29 +00:00
*
2012-04-05 20:22:47 +00:00
* /
2012-07-22 14:26:38 +00:00
private void initializeLoader ( )
2012-04-05 14:07:52 +00:00
{
File modsDir = new File ( minecraftDir , " mods " ) ;
File configDir = new File ( minecraftDir , " config " ) ;
String canonicalModsPath ;
String canonicalConfigPath ;
try
{
canonicalModsPath = modsDir . getCanonicalPath ( ) ;
canonicalConfigPath = configDir . getCanonicalPath ( ) ;
canonicalConfigDir = configDir . getCanonicalFile ( ) ;
2012-07-22 14:26:38 +00:00
canonicalModsDir = modsDir . getCanonicalFile ( ) ;
2012-04-05 14:07:52 +00:00
}
catch ( IOException ioe )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , ioe , " Failed to resolve loader directories: mods : %s ; config %s " , canonicalModsDir . getAbsolutePath ( ) ,
2012-07-31 02:31:07 +00:00
configDir . getAbsolutePath ( ) ) ;
2012-04-05 14:07:52 +00:00
throw new LoaderException ( ioe ) ;
}
2012-07-22 14:26:38 +00:00
if ( ! canonicalModsDir . exists ( ) )
2012-04-05 14:07:52 +00:00
{
2012-07-30 02:54:59 +00:00
FMLLog . info ( " No mod directory found, creating one: %s " , canonicalModsPath ) ;
2012-07-22 14:26:38 +00:00
boolean dirMade = canonicalModsDir . mkdir ( ) ;
if ( ! dirMade )
2012-04-05 14:07:52 +00:00
{
2012-07-30 02:54:59 +00:00
FMLLog . severe ( " Unable to create the mod directory %s " , canonicalModsPath ) ;
2015-03-27 08:17:57 +00:00
throw new LoaderException ( String . format ( " Unable to create the mod directory %s " , canonicalModsPath ) ) ;
2012-04-05 14:07:52 +00:00
}
2012-07-30 02:54:59 +00:00
FMLLog . info ( " Mod directory created successfully " ) ;
2012-04-05 14:07:52 +00:00
}
2012-07-22 14:26:38 +00:00
if ( ! canonicalConfigDir . exists ( ) )
2012-04-05 14:07:52 +00:00
{
2012-07-30 02:54:59 +00:00
FMLLog . fine ( " No config directory found, creating one: %s " , canonicalConfigPath ) ;
2012-07-22 14:26:38 +00:00
boolean dirMade = canonicalConfigDir . mkdir ( ) ;
if ( ! dirMade )
2012-04-05 14:07:52 +00:00
{
2012-07-30 02:54:59 +00:00
FMLLog . severe ( " Unable to create the config directory %s " , canonicalConfigPath ) ;
2012-07-22 14:26:38 +00:00
throw new LoaderException ( ) ;
2012-04-05 14:07:52 +00:00
}
2012-07-30 02:54:59 +00:00
FMLLog . info ( " Config directory created successfully " ) ;
2012-04-05 14:07:52 +00:00
}
2012-07-22 14:26:38 +00:00
if ( ! canonicalModsDir . isDirectory ( ) )
2012-04-05 14:07:52 +00:00
{
2012-07-30 02:54:59 +00:00
FMLLog . severe ( " Attempting to load mods from %s, which is not a directory " , canonicalModsPath ) ;
2012-07-22 14:26:38 +00:00
throw new LoaderException ( ) ;
2012-04-05 14:07:52 +00:00
}
if ( ! configDir . isDirectory ( ) )
{
2012-07-30 02:54:59 +00:00
FMLLog . severe ( " Attempting to load configuration from %s, which is not a directory " , canonicalConfigPath ) ;
2012-07-22 14:26:38 +00:00
throw new LoaderException ( ) ;
2012-03-28 20:44:36 +00:00
}
2015-04-25 05:30:17 +00:00
readInjectedDependencies ( ) ;
2012-03-28 20:44:36 +00:00
}
2012-04-05 14:07:52 +00:00
2012-07-24 01:20:37 +00:00
public List < ModContainer > getModList ( )
2012-04-05 14:07:52 +00:00
{
2012-10-18 18:41:32 +00:00
return instance ( ) . mods ! = null ? ImmutableList . copyOf ( instance ( ) . mods ) : ImmutableList . < ModContainer > of ( ) ;
2012-03-28 20:44:36 +00:00
}
2012-04-05 14:07:52 +00:00
2016-07-20 20:03:56 +00:00
/ * *
* Used to setup a testharness with a single dummy mod instance for use with various testing hooks
2016-11-28 04:58:19 +00:00
* @param containers A list of dummy containers that will be returned as " active " for all queries
2016-07-20 20:03:56 +00:00
* /
2016-11-28 04:58:19 +00:00
public void setupTestHarness ( ModContainer . . . containers )
2016-07-20 20:03:56 +00:00
{
modController = new LoadController ( this ) ;
2016-11-28 04:58:19 +00:00
mods = Lists . newArrayList ( containers ) ;
namedMods = Maps . uniqueIndex ( mods , new ModIdFunction ( ) ) ;
2016-07-20 20:03:56 +00:00
modController . transition ( LoaderState . LOADING , false ) ;
modController . transition ( LoaderState . CONSTRUCTING , false ) ;
ObjectHolderRegistry . INSTANCE . findObjectHolders ( new ASMDataTable ( ) ) ;
2016-11-28 04:58:19 +00:00
modController . forceActiveContainer ( containers [ 0 ] ) ;
2016-07-20 20:03:56 +00:00
}
2012-04-05 20:22:47 +00:00
/ * *
2012-07-22 14:26:38 +00:00
* Called from the hook to start mod loading . We trigger the
2012-09-16 01:04:56 +00:00
* { @link # identifyMods ( ) } and Constructing , Preinitalization , and Initalization phases here . Finally ,
2012-07-22 14:26:38 +00:00
* the mod list is frozen completely and is consider immutable from then on .
2016-07-20 20:03:56 +00:00
* @param injectedModContainers containers to inject
2012-04-05 20:22:47 +00:00
* /
2016-06-13 13:33:26 +00:00
public void loadMods ( List < String > injectedModContainers )
2012-04-05 14:07:52 +00:00
{
2015-04-24 21:11:49 +00:00
progressBar = ProgressManager . push ( " Loading " , 7 ) ;
2015-04-24 20:00:22 +00:00
progressBar . step ( " Constructing Mods " ) ;
2012-07-22 14:26:38 +00:00
initializeLoader ( ) ;
2012-08-02 04:38:30 +00:00
mods = Lists . newArrayList ( ) ;
namedMods = Maps . newHashMap ( ) ;
2012-07-30 21:01:27 +00:00
modController = new LoadController ( this ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . LOADING , false ) ;
2016-06-13 13:33:26 +00:00
discoverer = identifyMods ( injectedModContainers ) ;
2014-05-08 14:22:02 +00:00
ModAPIManager . INSTANCE . manageAPI ( modClassLoader , discoverer ) ;
2012-07-22 14:26:38 +00:00
disableRequestedMods ( ) ;
2012-08-23 18:33:54 +00:00
modController . distributeStateMessage ( FMLLoadEvent . class ) ;
2016-05-04 01:26:52 +00:00
checkJavaCompatibility ( ) ;
2012-04-05 14:07:52 +00:00
sortModList ( ) ;
2013-10-31 02:20:28 +00:00
ModAPIManager . INSTANCE . cleanupAPIContainers ( modController . getActiveModList ( ) ) ;
ModAPIManager . INSTANCE . cleanupAPIContainers ( mods ) ;
2012-07-22 14:26:38 +00:00
mods = ImmutableList . copyOf ( mods ) ;
2014-05-08 14:22:02 +00:00
for ( File nonMod : discoverer . getNonModLibs ( ) )
2012-08-31 00:10:13 +00:00
{
if ( nonMod . isFile ( ) )
{
2012-10-24 13:41:46 +00:00
FMLLog . info ( " FML has found a non-mod file %s in your mods directory. It will now be injected into your classpath. This could severe stability issues, it should be removed if possible. " , nonMod . getName ( ) ) ;
2012-08-31 00:10:13 +00:00
try
{
modClassLoader . addFile ( nonMod ) ;
}
catch ( MalformedURLException e )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , e , " Encountered a weird problem with non-mod file injection : %s " , nonMod . getName ( ) ) ;
2012-08-31 00:10:13 +00:00
}
}
}
2016-10-11 05:15:35 +00:00
ConfigManager . loadData ( discoverer . getASMTable ( ) ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . CONSTRUCTING , false ) ;
2014-05-08 14:22:02 +00:00
modController . distributeStateMessage ( LoaderState . CONSTRUCTING , modClassLoader , discoverer . getASMTable ( ) , reverseDependencies ) ;
2015-06-10 05:25:13 +00:00
List < ModContainer > mods = Lists . newArrayList ( ) ;
mods . addAll ( getActiveModList ( ) ) ;
Collections . sort ( mods , new Comparator < ModContainer > ( )
{
@Override
public int compare ( ModContainer o1 , ModContainer o2 )
{
return o1 . getModId ( ) . compareTo ( o2 . getModId ( ) ) ;
}
} ) ;
2013-01-30 23:57:21 +00:00
FMLLog . fine ( " Mod signature data " ) ;
2015-06-10 05:25:13 +00:00
FMLLog . fine ( " \ tValid Signatures: " ) ;
for ( ModContainer mod : getActiveModList ( ) )
{
if ( mod . getSigningCertificate ( ) ! = null )
FMLLog . fine ( " \ t \ t(%s) %s \ t(%s \ t%s) \ t%s " , CertificateHelper . getFingerprint ( mod . getSigningCertificate ( ) ) , mod . getModId ( ) , mod . getName ( ) , mod . getVersion ( ) , mod . getSource ( ) . getName ( ) ) ;
}
FMLLog . fine ( " \ tMissing Signatures: " ) ;
2012-12-18 00:55:46 +00:00
for ( ModContainer mod : getActiveModList ( ) )
{
2015-06-10 05:25:13 +00:00
if ( mod . getSigningCertificate ( ) = = null )
FMLLog . fine ( " \ t \ t%s \ t(%s \ t%s) \ t%s " , mod . getModId ( ) , mod . getName ( ) , mod . getVersion ( ) , mod . getSource ( ) . getName ( ) ) ;
2012-12-18 00:55:46 +00:00
}
2013-01-31 01:55:12 +00:00
if ( getActiveModList ( ) . isEmpty ( ) )
{
FMLLog . fine ( " No user mod signature data found " ) ;
}
2015-04-24 20:00:22 +00:00
progressBar . step ( " Initializing mods Phase 1 " ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . PREINITIALIZATION , false ) ;
2014-05-08 14:22:02 +00:00
}
2016-05-04 01:26:52 +00:00
private void checkJavaCompatibility ( )
{
if ( java8 ) return ;
List < ModContainer > j8mods = Lists . newArrayList ( ) ;
for ( ModContainer mc : getActiveModList ( ) )
{
if ( mc . getClassVersion ( ) > = 52 )
{
j8mods . add ( mc ) ;
}
}
if ( ! j8mods . isEmpty ( ) )
{
throw new Java8VersionException ( j8mods ) ;
}
}
2014-05-08 14:22:02 +00:00
public void preinitializeMods ( )
{
2014-05-08 14:40:52 +00:00
if ( ! modController . isInState ( LoaderState . PREINITIALIZATION ) )
{
FMLLog . warning ( " There were errors previously. Not beginning mod initialization phase " ) ;
return ;
}
2016-10-09 20:34:38 +00:00
PersistentRegistryManager . fireCreateRegistryEvents ( ) ;
2014-07-12 16:03:34 +00:00
ObjectHolderRegistry . INSTANCE . findObjectHolders ( discoverer . getASMTable ( ) ) ;
2015-05-30 18:07:54 +00:00
ItemStackHolderInjector . INSTANCE . findHolders ( discoverer . getASMTable ( ) ) ;
2016-01-12 00:04:22 +00:00
CapabilityManager . INSTANCE . injectCapabilities ( discoverer . getASMTable ( ) ) ;
2016-09-18 01:08:42 +00:00
PersistentRegistryManager . fireRegistryEvents ( ) ;
FMLCommonHandler . instance ( ) . fireSidedRegistryEvents ( ) ;
2014-05-08 14:22:02 +00:00
modController . distributeStateMessage ( LoaderState . PREINITIALIZATION , discoverer . getASMTable ( ) , canonicalConfigDir ) ;
2014-05-25 02:32:24 +00:00
ObjectHolderRegistry . INSTANCE . applyObjectHolders ( ) ;
2015-05-30 18:07:54 +00:00
ItemStackHolderInjector . INSTANCE . inject ( ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . INITIALIZATION , false ) ;
2015-04-24 21:11:49 +00:00
progressBar . step ( " Initializing Minecraft Engine " ) ;
2012-03-28 20:44:36 +00:00
}
2012-07-22 14:26:38 +00:00
private void disableRequestedMods ( )
2012-04-05 14:07:52 +00:00
{
2012-08-23 21:37:44 +00:00
String forcedModList = System . getProperty ( " fml.modStates " , " " ) ;
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Received a system property request \ '%s \ ' " , forcedModList ) ;
2012-07-22 14:26:38 +00:00
Map < String , String > sysPropertyStateList = Splitter . on ( CharMatcher . anyOf ( " ;: " ) )
. omitEmptyStrings ( ) . trimResults ( ) . withKeyValueSeparator ( " = " )
2012-08-23 21:37:44 +00:00
. split ( forcedModList ) ;
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " System property request managing the state of %d mods " , sysPropertyStateList . size ( ) ) ;
2012-07-22 14:26:38 +00:00
Map < String , String > modStates = Maps . newHashMap ( ) ;
2012-07-30 02:54:59 +00:00
2013-12-17 15:51:00 +00:00
forcedModFile = new File ( canonicalConfigDir , " fmlModState.properties " ) ;
2012-08-23 21:37:44 +00:00
Properties forcedModListProperties = new Properties ( ) ;
if ( forcedModFile . exists ( ) & & forcedModFile . isFile ( ) )
2012-07-22 14:26:38 +00:00
{
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Found a mod state file %s " , forcedModFile . getName ( ) ) ;
2012-07-22 14:26:38 +00:00
try
{
2012-08-23 21:37:44 +00:00
forcedModListProperties . load ( new FileReader ( forcedModFile ) ) ;
2013-01-30 23:57:21 +00:00
FMLLog . finer ( " Loaded states for %d mods from file " , forcedModListProperties . size ( ) ) ;
2012-07-22 14:26:38 +00:00
}
catch ( Exception e )
{
2012-07-30 02:54:59 +00:00
FMLLog . log ( Level . INFO , e , " An error occurred reading the fmlModState.properties file " ) ;
2012-07-22 14:26:38 +00:00
}
}
2012-08-23 21:37:44 +00:00
modStates . putAll ( Maps . fromProperties ( forcedModListProperties ) ) ;
2012-07-22 14:26:38 +00:00
modStates . putAll ( sysPropertyStateList ) ;
2012-07-30 02:54:59 +00:00
FMLLog . fine ( " After merging, found state information for %d mods " , modStates . size ( ) ) ;
2012-07-22 14:26:38 +00:00
Map < String , Boolean > isEnabled = Maps . transformValues ( modStates , new Function < String , Boolean > ( )
{
2014-01-18 04:19:22 +00:00
@Override
2012-07-22 14:26:38 +00:00
public Boolean apply ( String input )
{
2012-08-23 21:37:44 +00:00
return Boolean . parseBoolean ( input ) ;
2012-07-22 14:26:38 +00:00
}
} ) ;
2012-07-30 02:54:59 +00:00
2012-07-22 14:26:38 +00:00
for ( Map . Entry < String , Boolean > entry : isEnabled . entrySet ( ) )
{
if ( namedMods . containsKey ( entry . getKey ( ) ) )
{
2012-07-30 02:54:59 +00:00
FMLLog . info ( " Setting mod %s to enabled state %b " , entry . getKey ( ) , entry . getValue ( ) ) ;
2012-07-22 14:26:38 +00:00
namedMods . get ( entry . getKey ( ) ) . setEnabledState ( entry . getValue ( ) ) ;
}
2012-05-10 13:42:31 +00:00
}
2012-03-28 20:44:36 +00:00
}
2012-04-05 14:07:52 +00:00
2012-04-05 20:22:47 +00:00
/ * *
* Query if we know of a mod named modname
2012-07-30 02:54:59 +00:00
*
2012-04-05 20:22:47 +00:00
* @param modname
2012-09-16 01:04:56 +00:00
* @return If the mod is loaded
2012-04-05 20:22:47 +00:00
* /
2012-04-05 14:07:52 +00:00
public static boolean isModLoaded ( String modname )
{
2012-08-23 21:08:18 +00:00
return instance ( ) . namedMods . containsKey ( modname ) & & instance ( ) . modController . getModState ( instance . namedMods . get ( modname ) ) ! = ModState . DISABLED ;
2012-03-28 20:44:36 +00:00
}
2012-04-05 20:22:47 +00:00
2012-04-05 14:07:52 +00:00
public File getConfigDir ( )
{
return canonicalConfigDir ;
2012-03-28 20:44:36 +00:00
}
2012-06-26 20:24:50 +00:00
2012-05-06 23:32:59 +00:00
public String getCrashInformation ( )
{
2013-03-10 16:12:22 +00:00
// Handle being called before we've begun setup
if ( modController = = null )
{
return " " ;
}
2012-07-22 14:26:38 +00:00
StringBuilder ret = new StringBuilder ( ) ;
2013-12-06 16:17:40 +00:00
List < String > branding = FMLCommonHandler . instance ( ) . getBrandings ( false ) ;
2012-07-30 02:54:59 +00:00
2013-12-06 16:17:40 +00:00
Joiner . on ( ' ' ) . skipNulls ( ) . appendTo ( ret , branding ) ;
2013-08-28 17:18:28 +00:00
if ( modController ! = null )
2012-07-23 19:03:17 +00:00
{
modController . printModStates ( ret ) ;
}
2012-05-10 13:42:31 +00:00
return ret . toString ( ) ;
2012-05-06 23:32:59 +00:00
}
2012-05-11 05:45:36 +00:00
public String getFMLVersionString ( )
{
2015-06-01 20:29:34 +00:00
return " 8.0.99.99 " ;
2012-05-11 05:45:36 +00:00
}
2012-05-15 19:52:19 +00:00
2015-12-01 18:08:35 +00:00
public ModClassLoader getModClassLoader ( )
2012-05-15 19:52:19 +00:00
{
return modClassLoader ;
}
2012-07-22 14:26:38 +00:00
2012-08-23 17:43:25 +00:00
public void computeDependencies ( String dependencyString , Set < ArtifactVersion > requirements , List < ArtifactVersion > dependencies , List < ArtifactVersion > dependants )
2012-07-22 14:26:38 +00:00
{
if ( dependencyString = = null | | dependencyString . length ( ) = = 0 )
{
return ;
}
2012-07-30 02:54:59 +00:00
2013-08-28 17:18:28 +00:00
boolean parseFailure = false ;
2012-07-30 02:54:59 +00:00
2012-07-22 14:26:38 +00:00
for ( String dep : DEPENDENCYSPLITTER . split ( dependencyString ) )
{
List < String > depparts = Lists . newArrayList ( DEPENDENCYPARTSPLITTER . split ( dep ) ) ;
2012-07-30 21:01:27 +00:00
// Need two parts to the string
2012-07-22 14:26:38 +00:00
if ( depparts . size ( ) ! = 2 )
{
2013-08-28 17:18:28 +00:00
parseFailure = true ;
2012-07-22 14:26:38 +00:00
continue ;
}
2012-07-23 19:03:17 +00:00
String instruction = depparts . get ( 0 ) ;
String target = depparts . get ( 1 ) ;
2012-07-30 21:01:27 +00:00
boolean targetIsAll = target . startsWith ( " * " ) ;
// Cannot have an "all" relationship with anything except pure *
2013-08-28 17:18:28 +00:00
if ( targetIsAll & & target . length ( ) > 1 )
2012-07-30 21:01:27 +00:00
{
parseFailure = true ;
continue ;
}
2012-07-30 02:54:59 +00:00
2012-07-22 14:26:38 +00:00
// If this is a required element, add it to the required list
if ( " required-before " . equals ( instruction ) | | " required-after " . equals ( instruction ) )
{
// You can't require everything
if ( ! targetIsAll )
{
2012-07-30 21:01:27 +00:00
requirements . add ( VersionParser . parseVersionReference ( target ) ) ;
2012-07-22 14:26:38 +00:00
}
else
{
2013-08-28 17:18:28 +00:00
parseFailure = true ;
2012-07-22 14:26:38 +00:00
continue ;
}
}
2012-07-30 02:54:59 +00:00
2012-07-30 21:01:27 +00:00
// You cannot have a versioned dependency on everything
2013-08-28 17:18:28 +00:00
if ( targetIsAll & & target . indexOf ( '@' ) > - 1 )
2012-07-30 21:01:27 +00:00
{
parseFailure = true ;
continue ;
}
2012-07-22 14:26:38 +00:00
// before elements are things we are loaded before (so they are our dependants)
if ( " required-before " . equals ( instruction ) | | " before " . equals ( instruction ) )
{
2016-03-09 22:52:32 +00:00
dependants . add ( VersionParser . parseVersionReference ( target ) ) ;
2012-07-22 14:26:38 +00:00
}
// after elements are things that load before we do (so they are out dependencies)
else if ( " required-after " . equals ( instruction ) | | " after " . equals ( instruction ) )
{
2012-07-30 21:01:27 +00:00
dependencies . add ( VersionParser . parseVersionReference ( target ) ) ;
2012-07-22 14:26:38 +00:00
}
else
{
2013-08-28 17:18:28 +00:00
parseFailure = true ;
2012-07-22 14:26:38 +00:00
}
}
2012-07-30 02:54:59 +00:00
2012-07-22 14:26:38 +00:00
if ( parseFailure )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . WARN , " Unable to parse dependency string %s " , dependencyString ) ;
2015-03-27 08:17:57 +00:00
throw new LoaderException ( String . format ( " Unable to parse dependency string %s " , dependencyString ) ) ;
2012-07-22 14:26:38 +00:00
}
}
public Map < String , ModContainer > getIndexedModList ( )
{
return ImmutableMap . copyOf ( namedMods ) ;
}
2012-07-23 19:03:17 +00:00
public void initializeMods ( )
{
2015-05-03 02:13:41 +00:00
progressBar . step ( " Initializing mods Phase 2 " ) ;
2012-07-23 19:03:17 +00:00
// Mod controller should be in the initialization state here
2012-08-02 04:38:30 +00:00
modController . distributeStateMessage ( LoaderState . INITIALIZATION ) ;
2015-04-24 20:00:22 +00:00
progressBar . step ( " Initializing mods Phase 3 " ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . POSTINITIALIZATION , false ) ;
2012-10-24 20:39:55 +00:00
modController . distributeStateMessage ( FMLInterModComms . IMCEvent . class ) ;
2015-05-30 18:07:54 +00:00
ItemStackHolderInjector . INSTANCE . inject ( ) ;
2012-08-02 04:38:30 +00:00
modController . distributeStateMessage ( LoaderState . POSTINITIALIZATION ) ;
2015-04-21 03:34:36 +00:00
progressBar . step ( " Finishing up " ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . AVAILABLE , false ) ;
2012-08-02 04:38:30 +00:00
modController . distributeStateMessage ( LoaderState . AVAILABLE ) ;
2015-11-28 08:01:31 +00:00
PersistentRegistryManager . freezeData ( ) ;
2013-08-28 17:18:28 +00:00
FMLLog . info ( " Forge Mod Loader has successfully loaded %d mod%s " , mods . size ( ) , mods . size ( ) = = 1 ? " " : " s " ) ;
2015-04-24 21:47:39 +00:00
progressBar . step ( " Completing Minecraft initialization " ) ;
2012-07-23 19:03:17 +00:00
}
2012-07-24 01:20:37 +00:00
2012-08-21 22:48:12 +00:00
public ICrashCallable getCallableCrashInformation ( )
2012-07-24 01:20:37 +00:00
{
2012-08-21 22:48:12 +00:00
return new ICrashCallable ( ) {
2012-07-24 01:20:37 +00:00
@Override
public String call ( ) throws Exception
{
return getCrashInformation ( ) ;
}
2012-08-21 22:48:12 +00:00
@Override
public String getLabel ( )
{
return " FML " ;
}
2012-07-24 01:20:37 +00:00
} ;
}
public List < ModContainer > getActiveModList ( )
{
2012-10-18 18:41:32 +00:00
return modController ! = null ? modController . getActiveModList ( ) : ImmutableList . < ModContainer > of ( ) ;
2012-07-24 01:20:37 +00:00
}
public ModState getModState ( ModContainer selectedMod )
{
return modController . getModState ( selectedMod ) ;
}
2012-07-31 02:31:07 +00:00
public String getMCVersionString ( )
{
return " Minecraft " + mccversion ;
}
2012-08-02 04:38:30 +00:00
2013-01-22 14:11:44 +00:00
public boolean serverStarting ( Object server )
2012-08-02 04:38:30 +00:00
{
2013-01-22 14:11:44 +00:00
try
{
modController . distributeStateMessage ( LoaderState . SERVER_STARTING , server ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . SERVER_STARTING , false ) ;
2013-01-22 14:11:44 +00:00
}
catch ( Throwable t )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , t , " A fatal exception occurred during the server starting event " ) ;
2013-01-22 14:11:44 +00:00
return false ;
}
return true ;
2012-08-02 04:38:30 +00:00
}
2012-08-04 15:26:51 +00:00
2012-08-02 04:38:30 +00:00
public void serverStarted ( )
{
modController . distributeStateMessage ( LoaderState . SERVER_STARTED ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . SERVER_STARTED , false ) ;
2012-08-02 04:38:30 +00:00
}
2012-08-04 15:26:51 +00:00
2012-08-02 04:38:30 +00:00
public void serverStopping ( )
{
modController . distributeStateMessage ( LoaderState . SERVER_STOPPING ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . SERVER_STOPPING , false ) ;
2012-08-02 04:38:30 +00:00
}
2012-08-11 06:43:04 +00:00
public BiMap < ModContainer , Object > getModObjectList ( )
{
return modController . getModObjectList ( ) ;
}
public BiMap < Object , ModContainer > getReversedModObjectList ( )
{
return getModObjectList ( ) . inverse ( ) ;
}
2012-08-13 19:26:29 +00:00
public ModContainer activeModContainer ( )
{
2012-12-07 06:52:16 +00:00
return modController ! = null ? modController . activeContainer ( ) : null ;
2012-08-13 19:26:29 +00:00
}
public boolean isInState ( LoaderState state )
{
return modController . isInState ( state ) ;
}
2012-10-02 05:29:46 +00:00
public MinecraftDummyContainer getMinecraftModContainer ( )
{
return minecraft ;
}
2012-10-24 20:39:55 +00:00
2013-08-28 17:18:28 +00:00
public boolean hasReachedState ( LoaderState state )
{
return modController ! = null ? modController . hasReachedState ( state ) : false ;
}
2012-10-25 13:02:49 +00:00
2013-08-28 17:18:28 +00:00
public String getMCPVersionString ( )
{
2015-11-13 07:59:43 +00:00
return String . format ( " MCP %s " , mcpversion ) ;
2013-08-28 17:18:28 +00:00
}
2012-12-31 04:52:38 +00:00
public void serverStopped ( )
{
2015-11-28 08:01:31 +00:00
PersistentRegistryManager . revertToFrozen ( ) ;
2012-12-31 04:52:38 +00:00
modController . distributeStateMessage ( LoaderState . SERVER_STOPPED ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . SERVER_STOPPED , true ) ;
modController . transition ( LoaderState . AVAILABLE , true ) ;
2012-12-31 04:52:38 +00:00
}
2013-01-22 14:11:44 +00:00
2013-01-24 15:23:36 +00:00
public boolean serverAboutToStart ( Object server )
2013-01-22 14:11:44 +00:00
{
try
{
modController . distributeStateMessage ( LoaderState . SERVER_ABOUT_TO_START , server ) ;
2013-06-11 18:41:19 +00:00
modController . transition ( LoaderState . SERVER_ABOUT_TO_START , false ) ;
2013-01-22 14:11:44 +00:00
}
catch ( Throwable t )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , t , " A fatal exception occurred during the server about to start event " ) ;
2013-01-22 14:11:44 +00:00
return false ;
}
return true ;
}
2013-03-23 19:39:34 +00:00
2013-03-30 16:12:54 +00:00
public Map < String , String > getFMLBrandingProperties ( )
{
if ( fmlBrandingProperties = = null )
{
Properties loaded = new Properties ( ) ;
try
{
loaded . load ( getClass ( ) . getClassLoader ( ) . getResourceAsStream ( " fmlbranding.properties " ) ) ;
}
2013-03-31 14:01:46 +00:00
catch ( Exception e )
2013-03-30 16:12:54 +00:00
{
// File not found - ignore
}
fmlBrandingProperties = Maps . fromProperties ( loaded ) ;
}
return fmlBrandingProperties ;
}
2013-06-27 12:38:11 +00:00
public Map < String , String > getCustomModProperties ( String modId )
{
return getIndexedModList ( ) . get ( modId ) . getCustomModProperties ( ) ;
}
2013-12-09 06:22:24 +00:00
boolean checkRemoteModList ( Map < String , String > modList , Side side )
{
Set < String > remoteModIds = modList . keySet ( ) ;
Set < String > localModIds = namedMods . keySet ( ) ;
Set < String > difference = Sets . newLinkedHashSet ( Sets . difference ( localModIds , remoteModIds ) ) ;
for ( Iterator < String > iterator = difference . iterator ( ) ; iterator . hasNext ( ) ; )
{
String missingRemotely = iterator . next ( ) ;
ModState modState = modController . getModState ( namedMods . get ( missingRemotely ) ) ;
if ( modState = = ModState . DISABLED )
{
iterator . remove ( ) ;
}
}
2014-09-23 02:29:40 +00:00
if ( difference . size ( ) > 0 )
FMLLog . info ( " Attempting connection with missing mods %s at %s " , difference , side ) ;
2013-12-09 06:22:24 +00:00
return true ;
}
2013-12-11 00:25:49 +00:00
2014-03-24 20:16:36 +00:00
/ * *
* Fire a FMLMissingMappingsEvent to let mods determine how blocks / items defined in the world
* save , but missing from the runtime , are to be handled .
*
2016-05-29 21:05:07 +00:00
* @param missingBlocks Map containing the missing block names with their associated id . Remapped blocks will be removed from it .
* @param missingItems Map containing the missing block names with their associated id . Remapped items will be removed from it .
2014-03-24 20:16:36 +00:00
* @param isLocalWorld Whether this is executing for a world load ( local / server ) or a client .
2016-05-29 21:05:07 +00:00
* @param remapBlocks Returns a map containing the remapped block names and an array containing the original and new id for the block .
* @param remapItems Returns a map containing the remapped item names and an array containing the original and new id for the item .
* @return List with the names of the failed remappings .
2014-03-24 20:16:36 +00:00
* /
2015-11-28 08:01:31 +00:00
public List < String > fireMissingMappingEvent ( Map < ResourceLocation , Integer > missingBlocks , Map < ResourceLocation , Integer > missingItems , boolean isLocalWorld , Map < ResourceLocation , Integer [ ] > remapBlocks , Map < ResourceLocation , Integer [ ] > remapItems )
2013-12-18 13:34:20 +00:00
{
2014-12-15 14:57:59 +00:00
if ( missingBlocks . isEmpty ( ) & & missingItems . isEmpty ( ) ) // nothing to do
2013-12-18 13:34:20 +00:00
{
2014-03-24 20:16:36 +00:00
return ImmutableList . of ( ) ;
}
2014-12-15 14:57:59 +00:00
FMLLog . fine ( " There are %d mappings missing - attempting a mod remap " , missingBlocks . size ( ) + missingItems . size ( ) ) ;
2014-03-24 20:16:36 +00:00
ArrayListMultimap < String , MissingMapping > missingMappings = ArrayListMultimap . create ( ) ;
2015-11-28 08:01:31 +00:00
for ( Map . Entry < ResourceLocation , Integer > mapping : missingBlocks . entrySet ( ) )
2014-03-24 20:16:36 +00:00
{
2014-12-15 14:57:59 +00:00
MissingMapping m = new MissingMapping ( GameRegistry . Type . BLOCK , mapping . getKey ( ) , mapping . getValue ( ) ) ;
2015-11-28 08:01:31 +00:00
missingMappings . put ( m . resourceLocation . getResourceDomain ( ) , m ) ;
2014-12-15 14:57:59 +00:00
}
2015-11-28 08:01:31 +00:00
for ( Map . Entry < ResourceLocation , Integer > mapping : missingItems . entrySet ( ) )
2014-12-15 14:57:59 +00:00
{
MissingMapping m = new MissingMapping ( GameRegistry . Type . ITEM , mapping . getKey ( ) , mapping . getValue ( ) ) ;
2015-11-28 08:01:31 +00:00
missingMappings . put ( m . resourceLocation . getResourceDomain ( ) , m ) ;
2014-03-24 20:16:36 +00:00
}
FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent ( missingMappings ) ;
modController . propogateStateMessage ( missingEvent ) ;
if ( isLocalWorld ) // local world, warn about entries still being set to the default action
{
boolean didWarn = false ;
for ( MissingMapping mapping : missingMappings . values ( ) )
2014-02-08 17:18:34 +00:00
{
2014-03-24 20:16:36 +00:00
if ( mapping . getAction ( ) = = FMLMissingMappingsEvent . Action . DEFAULT )
2014-02-08 17:18:34 +00:00
{
2014-03-24 20:16:36 +00:00
if ( ! didWarn )
{
FMLLog . severe ( " There are unidentified mappings in this world - we are going to attempt to process anyway " ) ;
didWarn = true ;
}
FMLLog . severe ( " Unidentified %s: %s, id %d " , mapping . type = = Type . BLOCK ? " block " : " item " , mapping . name , mapping . id ) ;
2014-02-08 17:18:34 +00:00
}
}
2014-03-24 20:16:36 +00:00
}
else // remote world, fail on entries with the default action
{
List < String > missedMapping = new ArrayList < String > ( ) ;
for ( MissingMapping mapping : missingMappings . values ( ) )
2013-12-18 13:34:20 +00:00
{
2014-03-24 20:16:36 +00:00
if ( mapping . getAction ( ) = = FMLMissingMappingsEvent . Action . DEFAULT )
2014-02-08 17:18:34 +00:00
{
2014-03-24 20:16:36 +00:00
missedMapping . add ( mapping . name ) ;
2014-02-08 17:18:34 +00:00
}
2014-03-24 20:16:36 +00:00
}
if ( ! missedMapping . isEmpty ( ) )
{
2014-02-08 17:18:34 +00:00
return ImmutableList . copyOf ( missedMapping ) ;
2013-12-18 13:34:20 +00:00
}
}
2014-03-24 20:16:36 +00:00
2016-03-08 22:50:49 +00:00
return PersistentRegistryManager . processIdRematches ( missingMappings . values ( ) , isLocalWorld , missingBlocks , missingItems , remapBlocks , remapItems ) ;
2013-12-18 13:34:20 +00:00
}
2014-03-24 20:16:36 +00:00
2015-12-03 22:21:04 +00:00
public void fireRemapEvent ( Map < ResourceLocation , Integer [ ] > remapBlocks , Map < ResourceLocation , Integer [ ] > remapItems , boolean isFreezing )
2013-12-11 00:25:49 +00:00
{
2016-07-19 23:21:52 +00:00
if ( modController ! = null )
{
modController . propogateStateMessage ( new FMLModIdMappingEvent ( remapBlocks , remapItems , isFreezing ) ) ;
}
2013-12-11 00:25:49 +00:00
}
2013-12-17 15:51:00 +00:00
public void runtimeDisableMod ( String modId )
{
ModContainer mc = namedMods . get ( modId ) ;
Disableable disableable = mc . canBeDisabled ( ) ;
if ( disableable = = Disableable . NEVER )
{
FMLLog . info ( " Cannot disable mod %s - it is never allowed to be disabled " , modId ) ;
return ;
}
if ( disableable = = Disableable . DEPENDENCIES )
{
FMLLog . info ( " Cannot disable mod %s - there are dependent mods that require its presence " , modId ) ;
return ;
}
if ( disableable = = Disableable . YES )
{
FMLLog . info ( " Runtime disabling mod %s " , modId ) ;
modController . disableMod ( mc ) ;
List < ModContainer > localmods = Lists . newArrayList ( mods ) ;
localmods . remove ( mc ) ;
mods = ImmutableList . copyOf ( localmods ) ;
}
try
{
Properties props = new Properties ( ) ;
props . load ( new FileReader ( forcedModFile ) ) ;
props . put ( modId , " false " ) ;
props . store ( new FileWriter ( forcedModFile ) , null ) ;
}
catch ( Exception e )
{
FMLLog . log ( Level . INFO , e , " An error occurred writing the fml mod states file, your disabled change won't persist " ) ;
}
}
2015-04-24 21:11:49 +00:00
public void loadingComplete ( )
{
ProgressManager . pop ( progressBar ) ;
progressBar = null ;
}
2015-04-25 05:30:17 +00:00
private ListMultimap < String , ArtifactVersion > injectedBefore = ArrayListMultimap . create ( ) ;
private ListMultimap < String , ArtifactVersion > injectedAfter = ArrayListMultimap . create ( ) ;
private void readInjectedDependencies ( )
{
File injectedDepFile = new File ( getConfigDir ( ) , " injectedDependencies.json " ) ;
if ( ! injectedDepFile . exists ( ) )
{
FMLLog . getLogger ( ) . log ( Level . DEBUG , " File {} not found. No dependencies injected " , injectedDepFile . getAbsolutePath ( ) ) ;
return ;
}
JsonParser parser = new JsonParser ( ) ;
JsonElement injectedDeps ;
try
{
injectedDeps = parser . parse ( new FileReader ( injectedDepFile ) ) ;
for ( JsonElement el : injectedDeps . getAsJsonArray ( ) )
{
JsonObject jo = el . getAsJsonObject ( ) ;
String modId = jo . get ( " modId " ) . getAsString ( ) ;
JsonArray deps = jo . get ( " deps " ) . getAsJsonArray ( ) ;
for ( JsonElement dep : deps )
{
JsonObject depObj = dep . getAsJsonObject ( ) ;
String type = depObj . get ( " type " ) . getAsString ( ) ;
if ( type . equals ( " before " ) ) {
injectedBefore . put ( modId , VersionParser . parseVersionReference ( depObj . get ( " target " ) . getAsString ( ) ) ) ;
} else if ( type . equals ( " after " ) ) {
injectedAfter . put ( modId , VersionParser . parseVersionReference ( depObj . get ( " target " ) . getAsString ( ) ) ) ;
} else {
FMLLog . getLogger ( ) . log ( Level . ERROR , " Invalid dependency type {} " , type ) ;
throw new RuntimeException ( " Unable to parse type " ) ;
}
}
}
} catch ( Exception e )
{
FMLLog . getLogger ( ) . log ( Level . ERROR , " Unable to parse {} - skipping " , injectedDepFile ) ;
FMLLog . getLogger ( ) . throwing ( Level . ERROR , e ) ;
return ;
}
FMLLog . getLogger ( ) . log ( Level . DEBUG , " Loaded {} injected dependencies on modIds: {} " , injectedBefore . size ( ) , injectedBefore . keySet ( ) ) ;
}
List < ArtifactVersion > getInjectedBefore ( String modId )
{
return injectedBefore . get ( modId ) ;
}
List < ArtifactVersion > getInjectedAfter ( String modId )
{
return injectedAfter . get ( modId ) ;
}
2015-05-07 18:17:45 +00:00
public final LoaderState getLoaderState ( )
{
return modController ! = null ? modController . getState ( ) : LoaderState . NOINIT ;
}
2016-10-08 23:30:53 +00:00
public void setActiveModContainer ( ModContainer container )
{
this . modController . forceActiveContainer ( container ) ;
}
2015-04-29 18:37:09 +00:00
}