2012-03-30 10:11:13 -04:00
/*
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-03-30 10:11:13 -04:00
*/
2013-03-07 19:25:48 -05:00
2012-04-02 18:09:06 -04:00
package cpw.mods.fml.common ;
2012-03-28 16:44:36 -04:00
import java.io.File ;
2012-07-22 11:56:38 -02:30
import java.io.FileReader ;
2013-12-17 10:51:00 -05:00
import java.io.FileWriter ;
2012-03-28 16:44:36 -04:00
import java.io.IOException ;
2012-08-30 20:10:13 -04:00
import java.net.MalformedURLException ;
2012-08-14 13:36:29 -04:00
import java.util.Comparator ;
2013-12-09 01:22:24 -05:00
import java.util.Iterator ;
2012-03-28 16:44:36 -04:00
import java.util.List ;
2012-03-30 01:12:59 -04:00
import java.util.Map ;
2012-05-10 09:42:31 -04:00
import java.util.Properties ;
2012-08-23 13:43:25 -04:00
import java.util.Set ;
2013-12-16 11:47:48 -05:00
import org.apache.logging.log4j.Level ;
2012-07-22 11:56:38 -02:30
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 10:51:00 -05:00
import com.google.common.collect.ArrayListMultimap ;
2012-08-11 02:43:04 -04:00
import com.google.common.collect.BiMap ;
2012-08-23 13:43:25 -04:00
import com.google.common.collect.HashBiMap ;
2012-07-22 11:56:38 -02:30
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableMap ;
2012-08-14 13:36:29 -04:00
import com.google.common.collect.ImmutableMultiset ;
2012-10-24 09:41:46 -04:00
import com.google.common.collect.LinkedHashMultimap ;
2013-12-17 10:51:00 -05:00
import com.google.common.collect.ListMultimap ;
2012-07-22 11:56:38 -02:30
import com.google.common.collect.Lists ;
import com.google.common.collect.Maps ;
2013-12-17 10:51:00 -05:00
import com.google.common.collect.Multimaps ;
2012-08-14 13:36:29 -04:00
import com.google.common.collect.Multiset.Entry ;
import com.google.common.collect.Multisets ;
import com.google.common.collect.Ordering ;
2013-11-10 11:49:06 -05:00
import com.google.common.collect.SetMultimap ;
import com.google.common.collect.Sets ;
2012-08-14 13:36:29 -04:00
import com.google.common.collect.TreeMultimap ;
2012-07-23 22:50:37 -02:30
import cpw.mods.fml.common.LoaderState.ModState ;
2013-12-17 10:51:00 -05:00
import cpw.mods.fml.common.ModContainer.Disableable ;
2012-07-22 11:56:38 -02:30
import cpw.mods.fml.common.discovery.ModDiscoverer ;
2012-10-24 16:39:55 -04:00
import cpw.mods.fml.common.event.FMLInterModComms ;
2012-08-04 11:26:51 -04:00
import cpw.mods.fml.common.event.FMLLoadEvent ;
2013-12-18 08:34:20 -05:00
import cpw.mods.fml.common.event.FMLMissingMappingsEvent ;
import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping ;
2013-12-10 19:25:49 -05:00
import cpw.mods.fml.common.event.FMLModIdMappingEvent ;
2013-06-04 12:52:30 -04:00
import cpw.mods.fml.common.functions.ArtifactVersionNameFunction ;
2012-07-22 11:56:38 -02:30
import cpw.mods.fml.common.functions.ModIdFunction ;
2013-03-08 13:15:09 -05:00
import cpw.mods.fml.common.registry.GameData ;
2012-04-03 12:03:21 -04:00
import cpw.mods.fml.common.toposort.ModSorter ;
2012-07-14 13:58:46 -04:00
import cpw.mods.fml.common.toposort.ModSortingException ;
2013-05-27 11:03:23 -04:00
import cpw.mods.fml.common.toposort.ModSortingException.SortingExceptionData ;
2012-04-05 16:22:47 -04:00
import cpw.mods.fml.common.toposort.TopologicalSort ;
2012-07-30 17:01:27 -04:00
import cpw.mods.fml.common.versioning.ArtifactVersion ;
import cpw.mods.fml.common.versioning.VersionParser ;
2013-12-09 01:22:24 -05:00
import cpw.mods.fml.relauncher.Side ;
2012-04-05 16:22:47 -04:00
/**
* The loader class performs the actual loading of the mod code from disk.
2012-07-29 22:54:59 -04:00
*
2012-07-22 11:56:38 -02:30
* <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-29 22:54:59 -04:00
*
2012-04-05 16:22:47 -04:00
* <ol>
2012-07-22 11:56:38 -02:30
* <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 16:22:47 -04:00
* <li>UP. The Loader is complete</li>
2012-07-22 11:56:38 -02:30
* <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 16:22:47 -04:00
* </ol>
2012-07-29 22:54:59 -04:00
*
2012-07-22 11:56:38 -02:30
* Phase 1 code triggers the LOADING and PREINIT states. Phase 2 code triggers
* the INIT and POSTINIT states.
2012-07-29 22:54:59 -04:00
*
2012-04-05 16:22:47 -04:00
* @author cpw
2012-07-29 22:54:59 -04:00
*
2012-04-05 16:22:47 -04:00
*/
2012-04-05 10:07:52 -04:00
public class Loader
{
2012-07-22 11:56:38 -02:30
private static final Splitter DEPENDENCYPARTSPLITTER = Splitter . on ( ":" ). omitEmptyStrings (). trimResults ();
private static final Splitter DEPENDENCYSPLITTER = Splitter . on ( ";" ). omitEmptyStrings (). trimResults ();
2012-04-05 16:22:47 -04:00
/**
* The singleton instance
*/
2012-04-05 10:07:52 -04:00
private static Loader instance ;
2012-04-05 16:22:47 -04:00
/**
* Build information for tracking purposes.
*/
2012-05-10 09:42:31 -04:00
private static String major ;
private static String minor ;
private static String rev ;
private static String build ;
2012-07-02 12:24:37 -04:00
private static String mccversion ;
2012-10-25 09:02:49 -04:00
private static String mcpversion ;
2012-04-05 10:07:52 -04:00
2012-04-05 16:22:47 -04:00
/**
* The class loader we load the mods into.
*/
2012-04-05 10:07:52 -04:00
private ModClassLoader modClassLoader ;
2012-04-05 16:22:47 -04:00
/**
* The sorted list of mods.
*/
2012-04-05 10:07:52 -04:00
private List < ModContainer > mods ;
2012-04-05 16:22:47 -04:00
/**
* A named list of mods
*/
2012-04-05 10:07:52 -04:00
private Map < String , ModContainer > namedMods ;
2013-12-17 10:51:00 -05:00
/**
* A reverse dependency graph for mods
*/
private ListMultimap < String , String > reverseDependencies ;
2012-04-05 16:22:47 -04:00
/**
* The canonical configuration directory
*/
2012-04-05 10:07:52 -04:00
private File canonicalConfigDir ;
2012-07-22 11:56:38 -02:30
private File canonicalModsDir ;
private LoadController modController ;
2012-10-02 01:29:46 -04:00
private MinecraftDummyContainer minecraft ;
2013-08-28 10:18:28 -07:00
private MCPDummyContainer mcp ;
2012-06-26 16:24:50 -04:00
2012-07-30 22:31:07 -04:00
private static File minecraftDir ;
2012-08-04 11:26:51 -04:00
private static List < String > injectedContainers ;
2013-03-30 12:12:54 -04:00
private ImmutableMap < String , String > fmlBrandingProperties ;
2013-12-17 10:51:00 -05:00
private File forcedModFile ;
2012-07-30 22:31:07 -04:00
2012-04-05 10:07:52 -04:00
public static Loader instance ()
{
if ( instance == null )
{
instance = new Loader ();
}
return instance ;
2012-04-03 12:03:21 -04:00
}
2012-04-05 16:22:47 -04:00
2013-11-10 11:49:06 -05:00
@SuppressWarnings ( "unchecked" )
2012-07-30 22:31:07 -04:00
public static void injectData ( Object ... data )
2012-07-14 13:58:46 -04:00
{
2012-07-30 22:31:07 -04:00
major = ( String ) data [ 0 ] ;
minor = ( String ) data [ 1 ] ;
rev = ( String ) data [ 2 ] ;
build = ( String ) data [ 3 ] ;
mccversion = ( String ) data [ 4 ] ;
2012-10-25 09:02:49 -04:00
mcpversion = ( String ) data [ 5 ] ;
2012-07-30 22:31:07 -04:00
minecraftDir = ( File ) data [ 6 ] ;
2012-08-04 11:26:51 -04:00
injectedContainers = ( List < String > ) data [ 7 ] ;
2012-07-30 22:31:07 -04:00
}
2012-07-29 22:54:59 -04:00
2012-07-30 22:31:07 -04:00
private Loader ()
{
2012-07-14 13:58:46 -04:00
modClassLoader = new ModClassLoader ( getClass (). getClassLoader ());
2013-12-02 23:46:42 -05:00
String actualMCVersion = "1.7.2" ;
2012-08-13 00:17:34 -04:00
if ( ! mccversion . equals ( actualMCVersion ))
{
FMLLog . severe ( "This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file" , mccversion , actualMCVersion );
throw new LoaderException ();
}
2012-10-02 01:29:46 -04:00
minecraft = new MinecraftDummyContainer ( actualMCVersion );
2012-10-25 09:02:49 -04:00
mcp = new MCPDummyContainer ( MetadataCollection . from ( getClass (). getResourceAsStream ( "/mcpmod.info" ), "MCP" ). getMetadataForId ( "mcp" , null ));
2012-07-14 13:58:46 -04:00
}
2012-04-05 16:22:47 -04:00
/**
2012-07-22 11:56:38 -02:30
* 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 16:22:47 -04:00
*/
2012-04-05 10:07:52 -04:00
private void sortModList ()
{
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Verifying mod requirements are satisfied" );
2012-07-14 13:58:46 -04:00
try
2012-04-05 10:07:52 -04:00
{
2012-08-23 13:43:25 -04:00
BiMap < String , ArtifactVersion > modVersions = HashBiMap . create ();
2012-08-23 14:33:54 -04:00
for ( ModContainer mod : getActiveModList ())
2012-04-05 10:07:52 -04:00
{
2012-07-30 17:01:27 -04:00
modVersions . put ( mod . getModId (), mod . getProcessedVersion ());
}
2013-12-17 10:51:00 -05:00
ArrayListMultimap < String , String > reqList = ArrayListMultimap . create ();
2012-08-23 14:33:54 -04:00
for ( ModContainer mod : getActiveModList ())
2012-07-30 17:01:27 -04:00
{
2012-10-02 01:29:46 -04: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 ());
throw new WrongMinecraftVersionException ( mod );
}
2013-06-04 12:52:30 -04:00
Map < String , ArtifactVersion > names = Maps . uniqueIndex ( mod . getRequirements (), new ArtifactVersionNameFunction ());
2012-08-23 15:29:17 -04:00
Set < ArtifactVersion > versionMissingMods = Sets . newHashSet ();
2013-12-17 10:51:00 -05:00
2012-10-02 01:29:46 -04:00
Set < String > missingMods = Sets . difference ( names . keySet (), modVersions . keySet ());
2012-08-23 13:43:25 -04:00
if ( ! missingMods . isEmpty ())
{
FMLLog . severe ( "The mod %s (%s) requires mods %s to be available" , mod . getModId (), mod . getName (), missingMods );
2012-08-23 15:29:17 -04:00
for ( String modid : missingMods )
{
versionMissingMods . add ( names . get ( modid ));
}
throw new MissingModsException ( versionMissingMods );
2012-08-23 13:43:25 -04:00
}
2013-12-17 10:51:00 -05:00
reqList . putAll ( mod . getModId (), names . keySet ());
2012-07-30 17:01:27 -04:00
ImmutableList < ArtifactVersion > allDeps = ImmutableList . < ArtifactVersion > builder (). addAll ( mod . getDependants ()). addAll ( mod . getDependencies ()). build ();
for ( ArtifactVersion v : allDeps )
2012-07-14 13:58:46 -04:00
{
2012-07-30 17:01:27 -04:00
if ( modVersions . containsKey ( v . getLabel ()))
{
if ( ! v . containsVersion ( modVersions . get ( v . getLabel ())))
{
2012-08-23 15:29:17 -04:00
versionMissingMods . add ( v );
2012-07-30 17:01:27 -04:00
}
}
2012-07-14 13:58:46 -04:00
}
2012-08-23 15:29:17 -04:00
if ( ! versionMissingMods . isEmpty ())
2012-08-23 13:43:25 -04:00
{
2012-09-07 19:08:28 -04:00
FMLLog . severe ( "The mod %s (%s) requires mod versions %s to be available" , mod . getModId (), mod . getName (), versionMissingMods );
2012-08-23 15:29:17 -04:00
throw new MissingModsException ( versionMissingMods );
2012-08-23 13:43:25 -04:00
}
2012-04-05 10:07:52 -04:00
}
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "All mod requirements are satisfied" );
2012-07-29 22:54:59 -04:00
2013-12-17 10:51:00 -05:00
reverseDependencies = Multimaps . invertFrom ( reqList , ArrayListMultimap . < String , String > create ());
2012-08-23 14:33:54 -04:00
ModSorter sorter = new ModSorter ( getActiveModList (), namedMods );
2012-04-05 10:07:52 -04:00
2012-07-14 13:58:46 -04:00
try
{
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Sorting mods into an ordered list" );
2012-08-23 15:29:17 -04:00
List < ModContainer > sortedMods = sorter . sort ();
2012-08-24 11:58:18 -04: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 15:29:17 -04:00
mods . removeAll ( sortedMods );
sortedMods . addAll ( mods );
mods = sortedMods ;
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Mod sorting completed successfully" );
2012-07-14 13:58:46 -04:00
}
catch ( ModSortingException sortException )
{
2012-07-29 22:54:59 -04:00
FMLLog . severe ( "A dependency cycle was detected in the input mod set so an ordering cannot be determined" );
2013-05-27 11:03:23 -04: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 11:47:48 -05:00
FMLLog . log ( Level . ERROR , sortException , "The full error" );
2013-05-27 10:46:22 -04:00
throw sortException ;
2012-07-14 13:58:46 -04:00
}
}
finally
2012-04-05 10:07:52 -04:00
{
2013-01-30 18:57:21 -05:00
FMLLog . fine ( "Mod sorting data" );
int unprintedMods = mods . size ();
2012-08-23 14:33:54 -04:00
for ( ModContainer mod : getActiveModList ())
2012-05-10 01:00:07 -07:00
{
2012-08-04 11:26:51 -04:00
if ( ! mod . isImmutable ())
{
2012-08-23 14:28:49 -04:00
FMLLog . fine ( "\t%s(%s:%s): %s (%s)" , mod . getModId (), mod . getName (), mod . getVersion (), mod . getSource (). getName (), mod . getSortingRules ());
2013-01-30 18:57:21 -05:00
unprintedMods -- ;
2012-08-04 11:26:51 -04:00
}
2012-07-23 16:33:17 -02:30
}
2013-01-30 18:57:21 -05:00
if ( unprintedMods == mods . size ())
2012-07-29 22:54:59 -04:00
{
2013-01-30 18:57:21 -05:00
FMLLog . fine ( "No user mods found to sort" );
2012-05-10 01:00:07 -07:00
}
2012-04-05 10:07:52 -04:00
}
2012-07-14 13:58:46 -04:00
2012-03-28 16:44:36 -04:00
}
2012-04-05 10:07:52 -04:00
2012-04-05 16:22:47 -04:00
/**
2012-07-22 11:56:38 -02:30
* The primary loading code
2012-07-29 22:54:59 -04:00
*
*
2012-07-22 11:56:38 -02:30
* The found resources are first loaded into the {@link #modClassLoader}
* (always) then scanned for class resources matching the specification
* above.
2012-07-29 22:54:59 -04:00
*
2012-07-22 11:56:38 -02:30
* If they provide the {@link Mod} annotation, they will be loaded as
2013-11-10 13:15:26 -05:00
* "FML mods"
2012-07-29 22:54:59 -04:00
*
2012-07-22 11:56:38 -02:30
* Finally, if they are successfully loaded as classes, they are then added
* to the available mod list.
2012-04-05 16:22:47 -04:00
*/
2012-08-06 09:52:42 -04:00
private ModDiscoverer identifyMods ()
2012-04-05 10:07:52 -04:00
{
2012-08-04 11:26:51 -04:00
FMLLog . fine ( "Building injected Mod Containers %s" , injectedContainers );
2012-10-25 09:02:49 -04:00
// Add in the MCP mod container
2012-10-26 07:55:23 -04:00
mods . add ( new InjectedModContainer ( mcp , new File ( "minecraft.jar" )));
2012-08-04 11:26:51 -04:00
for ( String cont : injectedContainers )
{
ModContainer mc ;
try
{
mc = ( ModContainer ) Class . forName ( cont , true , modClassLoader ). newInstance ();
}
catch ( Exception e )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , e , "A problem occured instantiating the injected mod container %s" , cont );
2012-08-04 11:26:51 -04:00
throw new LoaderException ( e );
}
2013-08-27 12:31:37 -04:00
mods . add ( new InjectedModContainer ( mc , mc . getSource ()));
2012-08-04 11:26:51 -04:00
}
2012-07-22 11:56:38 -02:30
ModDiscoverer discoverer = new ModDiscoverer ();
2012-07-29 22:54:59 -04:00
FMLLog . fine ( "Attempting to load mods contained in the minecraft jar file and associated classes" );
2012-07-22 11:56:38 -02:30
discoverer . findClasspathMods ( modClassLoader );
2012-07-29 22:54:59 -04:00
FMLLog . fine ( "Minecraft jar mods loaded successfully" );
2012-04-05 10:07:52 -04:00
2012-07-29 22:54:59 -04:00
FMLLog . info ( "Searching %s for mods" , canonicalModsDir . getAbsolutePath ());
2012-07-22 11:56:38 -02:30
discoverer . findModDirMods ( canonicalModsDir );
2013-07-02 23:03:01 -04:00
File versionSpecificModsDir = new File ( canonicalModsDir , mccversion );
if ( versionSpecificModsDir . isDirectory ())
{
FMLLog . info ( "Also searching %s for mods" , versionSpecificModsDir );
discoverer . findModDirMods ( versionSpecificModsDir );
}
2012-04-05 10:07:52 -04:00
2012-08-04 11:26:51 -04:00
mods . addAll ( discoverer . identifyMods ());
2012-08-14 13:36:29 -04:00
identifyDuplicates ( mods );
2012-07-22 11:56:38 -02:30
namedMods = Maps . uniqueIndex ( mods , new ModIdFunction ());
2012-07-29 22:54:59 -04:00
FMLLog . info ( "Forge Mod Loader has identified %d mod%s to load" , mods . size (), mods . size () != 1 ? "s" : "" );
2012-08-06 09:52:42 -04:00
return discoverer ;
2012-03-28 16:44:36 -04:00
}
2012-04-05 10:07:52 -04:00
2012-08-14 13:36:29 -04: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 09:41:46 -04:00
SetMultimap < ModContainer , File > dupes = LinkedHashMultimap . create ();
2012-08-14 13:36:29 -04: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 09:41:46 -04:00
dupes . putAll ( e . getElement (), dupsearch . get ( e . getElement ()));
2012-08-14 13:36:29 -04:00
}
}
2012-10-24 09:41:46 -04:00
if ( ! dupes . isEmpty ())
{
2013-08-28 10:18:28 -07:00
throw new DuplicateModsFoundException ( dupes );
2012-10-24 09:41:46 -04:00
}
2012-08-14 13:36:29 -04:00
}
2012-04-05 16:22:47 -04:00
/**
2013-01-21 21:37:29 -05:00
*
2012-04-05 16:22:47 -04:00
*/
2012-07-22 11:56:38 -02:30
private void initializeLoader ()
2012-04-05 10:07:52 -04: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 11:56:38 -02:30
canonicalModsDir = modsDir . getCanonicalFile ();
2012-04-05 10:07:52 -04:00
}
catch ( IOException ioe )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , ioe , "Failed to resolve loader directories: mods : %s ; config %s" , canonicalModsDir . getAbsolutePath (),
2012-07-30 22:31:07 -04:00
configDir . getAbsolutePath ());
2012-04-05 10:07:52 -04:00
throw new LoaderException ( ioe );
}
2012-07-22 11:56:38 -02:30
if ( ! canonicalModsDir . exists ())
2012-04-05 10:07:52 -04:00
{
2012-07-29 22:54:59 -04:00
FMLLog . info ( "No mod directory found, creating one: %s" , canonicalModsPath );
2012-07-22 11:56:38 -02:30
boolean dirMade = canonicalModsDir . mkdir ();
if ( ! dirMade )
2012-04-05 10:07:52 -04:00
{
2012-07-29 22:54:59 -04:00
FMLLog . severe ( "Unable to create the mod directory %s" , canonicalModsPath );
2012-07-22 11:56:38 -02:30
throw new LoaderException ();
2012-04-05 10:07:52 -04:00
}
2012-07-29 22:54:59 -04:00
FMLLog . info ( "Mod directory created successfully" );
2012-04-05 10:07:52 -04:00
}
2012-07-22 11:56:38 -02:30
if ( ! canonicalConfigDir . exists ())
2012-04-05 10:07:52 -04:00
{
2012-07-29 22:54:59 -04:00
FMLLog . fine ( "No config directory found, creating one: %s" , canonicalConfigPath );
2012-07-22 11:56:38 -02:30
boolean dirMade = canonicalConfigDir . mkdir ();
if ( ! dirMade )
2012-04-05 10:07:52 -04:00
{
2012-07-29 22:54:59 -04:00
FMLLog . severe ( "Unable to create the config directory %s" , canonicalConfigPath );
2012-07-22 11:56:38 -02:30
throw new LoaderException ();
2012-04-05 10:07:52 -04:00
}
2012-07-29 22:54:59 -04:00
FMLLog . info ( "Config directory created successfully" );
2012-04-05 10:07:52 -04:00
}
2012-07-22 11:56:38 -02:30
if ( ! canonicalModsDir . isDirectory ())
2012-04-05 10:07:52 -04:00
{
2012-07-29 22:54:59 -04:00
FMLLog . severe ( "Attempting to load mods from %s, which is not a directory" , canonicalModsPath );
2012-07-22 11:56:38 -02:30
throw new LoaderException ();
2012-04-05 10:07:52 -04:00
}
if ( ! configDir . isDirectory ())
{
2012-07-29 22:54:59 -04:00
FMLLog . severe ( "Attempting to load configuration from %s, which is not a directory" , canonicalConfigPath );
2012-07-22 11:56:38 -02:30
throw new LoaderException ();
2012-03-28 16:44:36 -04:00
}
}
2012-04-05 10:07:52 -04:00
2012-07-23 22:50:37 -02:30
public List < ModContainer > getModList ()
2012-04-05 10:07:52 -04:00
{
2012-10-18 14:41:32 -04:00
return instance (). mods != null ? ImmutableList . copyOf ( instance (). mods ) : ImmutableList . < ModContainer > of ();
2012-03-28 16:44:36 -04:00
}
2012-04-05 10:07:52 -04:00
2012-04-05 16:22:47 -04:00
/**
2012-07-22 11:56:38 -02:30
* Called from the hook to start mod loading. We trigger the
2012-09-15 18:04:56 -07:00
* {@link #identifyMods()} and Constructing, Preinitalization, and Initalization phases here. Finally,
2012-07-22 11:56:38 -02:30
* the mod list is frozen completely and is consider immutable from then on.
2012-04-05 16:22:47 -04:00
*/
2012-04-05 10:07:52 -04:00
public void loadMods ()
{
2012-07-22 11:56:38 -02:30
initializeLoader ();
2012-08-02 02:08:30 -02:30
mods = Lists . newArrayList ();
namedMods = Maps . newHashMap ();
2012-07-30 17:01:27 -04:00
modController = new LoadController ( this );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . LOADING , false );
2012-08-06 09:52:42 -04:00
ModDiscoverer disc = identifyMods ();
2013-10-30 22:20:28 -04:00
ModAPIManager . INSTANCE . manageAPI ( modClassLoader , disc );
2012-07-22 11:56:38 -02:30
disableRequestedMods ();
2012-08-23 14:33:54 -04:00
modController . distributeStateMessage ( FMLLoadEvent . class );
2012-04-05 10:07:52 -04:00
sortModList ();
2013-10-30 22:20:28 -04:00
ModAPIManager . INSTANCE . cleanupAPIContainers ( modController . getActiveModList ());
ModAPIManager . INSTANCE . cleanupAPIContainers ( mods );
2012-07-22 11:56:38 -02:30
mods = ImmutableList . copyOf ( mods );
2012-08-30 20:10:13 -04:00
for ( File nonMod : disc . getNonModLibs ())
{
if ( nonMod . isFile ())
{
2012-10-24 09:41:46 -04: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-30 20:10:13 -04:00
try
{
modClassLoader . addFile ( nonMod );
}
catch ( MalformedURLException e )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , e , "Encountered a weird problem with non-mod file injection : %s" , nonMod . getName ());
2012-08-30 20:10:13 -04:00
}
}
}
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . CONSTRUCTING , false );
2013-12-17 10:51:00 -05:00
modController . distributeStateMessage ( LoaderState . CONSTRUCTING , modClassLoader , disc . getASMTable (), reverseDependencies );
2013-01-30 18:57:21 -05:00
FMLLog . fine ( "Mod signature data" );
2012-12-17 19:55:46 -05:00
for ( ModContainer mod : getActiveModList ())
{
FMLLog . fine ( "\t%s(%s:%s): %s (%s)" , mod . getModId (), mod . getName (), mod . getVersion (), mod . getSource (). getName (), CertificateHelper . getFingerprint ( mod . getSigningCertificate ()));
}
2013-01-30 20:55:12 -05:00
if ( getActiveModList (). isEmpty ())
{
FMLLog . fine ( "No user mod signature data found" );
}
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . PREINITIALIZATION , false );
2012-08-10 17:42:43 -04:00
modController . distributeStateMessage ( LoaderState . PREINITIALIZATION , disc . getASMTable (), canonicalConfigDir );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . INITIALIZATION , false );
2012-03-28 16:44:36 -04:00
}
2012-07-22 11:56:38 -02:30
private void disableRequestedMods ()
2012-04-05 10:07:52 -04:00
{
2012-08-23 17:37:44 -04:00
String forcedModList = System . getProperty ( "fml.modStates" , "" );
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Received a system property request \'%s\'" , forcedModList );
2012-07-22 11:56:38 -02:30
Map < String , String > sysPropertyStateList = Splitter . on ( CharMatcher . anyOf ( ";:" ))
. omitEmptyStrings (). trimResults (). withKeyValueSeparator ( "=" )
2012-08-23 17:37:44 -04:00
. split ( forcedModList );
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "System property request managing the state of %d mods" , sysPropertyStateList . size ());
2012-07-22 11:56:38 -02:30
Map < String , String > modStates = Maps . newHashMap ();
2012-07-29 22:54:59 -04:00
2013-12-17 10:51:00 -05:00
forcedModFile = new File ( canonicalConfigDir , "fmlModState.properties" );
2012-08-23 17:37:44 -04:00
Properties forcedModListProperties = new Properties ();
if ( forcedModFile . exists () && forcedModFile . isFile ())
2012-07-22 11:56:38 -02:30
{
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Found a mod state file %s" , forcedModFile . getName ());
2012-07-22 11:56:38 -02:30
try
{
2012-08-23 17:37:44 -04:00
forcedModListProperties . load ( new FileReader ( forcedModFile ));
2013-01-30 18:57:21 -05:00
FMLLog . finer ( "Loaded states for %d mods from file" , forcedModListProperties . size ());
2012-07-22 11:56:38 -02:30
}
catch ( Exception e )
{
2012-07-29 22:54:59 -04:00
FMLLog . log ( Level . INFO , e , "An error occurred reading the fmlModState.properties file" );
2012-07-22 11:56:38 -02:30
}
}
2012-08-23 17:37:44 -04:00
modStates . putAll ( Maps . fromProperties ( forcedModListProperties ));
2012-07-22 11:56:38 -02:30
modStates . putAll ( sysPropertyStateList );
2012-07-29 22:54:59 -04:00
FMLLog . fine ( "After merging, found state information for %d mods" , modStates . size ());
2012-07-22 11:56:38 -02:30
Map < String , Boolean > isEnabled = Maps . transformValues ( modStates , new Function < String , Boolean > ()
{
2014-01-17 23:19:22 -05:00
@Override
2012-07-22 11:56:38 -02:30
public Boolean apply ( String input )
{
2012-08-23 17:37:44 -04:00
return Boolean . parseBoolean ( input );
2012-07-22 11:56:38 -02:30
}
});
2012-07-29 22:54:59 -04:00
2012-07-22 11:56:38 -02:30
for ( Map . Entry < String , Boolean > entry : isEnabled . entrySet ())
{
if ( namedMods . containsKey ( entry . getKey ()))
{
2012-07-29 22:54:59 -04:00
FMLLog . info ( "Setting mod %s to enabled state %b" , entry . getKey (), entry . getValue ());
2012-07-22 11:56:38 -02:30
namedMods . get ( entry . getKey ()). setEnabledState ( entry . getValue ());
}
2012-05-10 09:42:31 -04:00
}
2012-03-28 16:44:36 -04:00
}
2012-04-05 10:07:52 -04:00
2012-04-05 16:22:47 -04:00
/**
* Query if we know of a mod named modname
2012-07-29 22:54:59 -04:00
*
2012-04-05 16:22:47 -04:00
* @param modname
2012-09-15 18:04:56 -07:00
* @return If the mod is loaded
2012-04-05 16:22:47 -04:00
*/
2012-04-05 10:07:52 -04:00
public static boolean isModLoaded ( String modname )
{
2012-08-23 17:08:18 -04:00
return instance (). namedMods . containsKey ( modname ) && instance (). modController . getModState ( instance . namedMods . get ( modname )) != ModState . DISABLED ;
2012-03-28 16:44:36 -04:00
}
2012-04-05 16:22:47 -04:00
2012-04-05 10:07:52 -04:00
public File getConfigDir ()
{
return canonicalConfigDir ;
2012-03-28 16:44:36 -04:00
}
2012-06-26 16:24:50 -04:00
2012-05-06 16:32:59 -07:00
public String getCrashInformation ()
{
2013-03-10 12:12:22 -04:00
// Handle being called before we've begun setup
if ( modController == null )
{
return "" ;
}
2012-07-22 11:56:38 -02:30
StringBuilder ret = new StringBuilder ();
2013-12-06 11:17:40 -05:00
List < String > branding = FMLCommonHandler . instance (). getBrandings ( false );
2012-07-29 22:54:59 -04:00
2013-12-06 11:17:40 -05:00
Joiner . on ( ' ' ). skipNulls (). appendTo ( ret , branding );
2013-08-28 10:18:28 -07:00
if ( modController != null )
2012-07-23 16:33:17 -02:30
{
modController . printModStates ( ret );
}
2012-05-10 09:42:31 -04:00
return ret . toString ();
2012-05-06 16:32:59 -07:00
}
2012-05-11 01:45:36 -04:00
public String getFMLVersionString ()
{
2012-09-07 19:08:28 -04:00
return String . format ( "%s.%s.%s.%s" , major , minor , rev , build );
2012-05-11 01:45:36 -04:00
}
2012-05-15 15:52:19 -04:00
public ClassLoader getModClassLoader ()
{
return modClassLoader ;
}
2012-07-22 11:56:38 -02:30
2012-08-23 13:43:25 -04:00
public void computeDependencies ( String dependencyString , Set < ArtifactVersion > requirements , List < ArtifactVersion > dependencies , List < ArtifactVersion > dependants )
2012-07-22 11:56:38 -02:30
{
if ( dependencyString == null || dependencyString . length () == 0 )
{
return ;
}
2012-07-29 22:54:59 -04:00
2013-08-28 10:18:28 -07:00
boolean parseFailure = false ;
2012-07-29 22:54:59 -04:00
2012-07-22 11:56:38 -02:30
for ( String dep : DEPENDENCYSPLITTER . split ( dependencyString ))
{
List < String > depparts = Lists . newArrayList ( DEPENDENCYPARTSPLITTER . split ( dep ));
2012-07-30 17:01:27 -04:00
// Need two parts to the string
2012-07-22 11:56:38 -02:30
if ( depparts . size () != 2 )
{
2013-08-28 10:18:28 -07:00
parseFailure = true ;
2012-07-22 11:56:38 -02:30
continue ;
}
2012-07-23 16:33:17 -02:30
String instruction = depparts . get ( 0 );
String target = depparts . get ( 1 );
2012-07-30 17:01:27 -04:00
boolean targetIsAll = target . startsWith ( "*" );
// Cannot have an "all" relationship with anything except pure *
2013-08-28 10:18:28 -07:00
if ( targetIsAll && target . length () > 1 )
2012-07-30 17:01:27 -04:00
{
parseFailure = true ;
continue ;
}
2012-07-29 22:54:59 -04:00
2012-07-22 11:56:38 -02:30
// 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 17:01:27 -04:00
requirements . add ( VersionParser . parseVersionReference ( target ));
2012-07-22 11:56:38 -02:30
}
else
{
2013-08-28 10:18:28 -07:00
parseFailure = true ;
2012-07-22 11:56:38 -02:30
continue ;
}
}
2012-07-29 22:54:59 -04:00
2012-07-30 17:01:27 -04:00
// You cannot have a versioned dependency on everything
2013-08-28 10:18:28 -07:00
if ( targetIsAll && target . indexOf ( '@' ) > - 1 )
2012-07-30 17:01:27 -04:00
{
parseFailure = true ;
continue ;
}
2012-07-22 11:56:38 -02:30
// before elements are things we are loaded before (so they are our dependants)
if ( "required-before" . equals ( instruction ) || "before" . equals ( instruction ))
{
2012-07-30 17:01:27 -04:00
dependants . add ( VersionParser . parseVersionReference ( target ));
2012-07-22 11:56:38 -02:30
}
// 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 17:01:27 -04:00
dependencies . add ( VersionParser . parseVersionReference ( target ));
2012-07-22 11:56:38 -02:30
}
else
{
2013-08-28 10:18:28 -07:00
parseFailure = true ;
2012-07-22 11:56:38 -02:30
}
}
2012-07-29 22:54:59 -04:00
2012-07-22 11:56:38 -02:30
if ( parseFailure )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . WARN , "Unable to parse dependency string %s" , dependencyString );
2012-07-22 11:56:38 -02:30
throw new LoaderException ();
}
}
public Map < String , ModContainer > getIndexedModList ()
{
return ImmutableMap . copyOf ( namedMods );
}
2012-07-23 16:33:17 -02:30
public void initializeMods ()
{
// Mod controller should be in the initialization state here
2012-08-02 02:08:30 -02:30
modController . distributeStateMessage ( LoaderState . INITIALIZATION );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . POSTINITIALIZATION , false );
2012-10-24 16:39:55 -04:00
modController . distributeStateMessage ( FMLInterModComms . IMCEvent . class );
2012-08-02 02:08:30 -02:30
modController . distributeStateMessage ( LoaderState . POSTINITIALIZATION );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . AVAILABLE , false );
2012-08-02 02:08:30 -02:30
modController . distributeStateMessage ( LoaderState . AVAILABLE );
2014-03-20 12:41:59 +01:00
GameData . freezeData ();
2013-03-23 15:39:34 -04:00
// Dump the custom registry data map, if necessary
GameData . dumpRegistry ( minecraftDir );
2013-08-28 10:18:28 -07:00
FMLLog . info ( "Forge Mod Loader has successfully loaded %d mod%s" , mods . size (), mods . size () == 1 ? "" : "s" );
2012-07-23 16:33:17 -02:30
}
2012-07-23 22:50:37 -02:30
2012-08-21 18:48:12 -04:00
public ICrashCallable getCallableCrashInformation ()
2012-07-23 22:50:37 -02:30
{
2012-08-21 18:48:12 -04:00
return new ICrashCallable () {
2012-07-23 22:50:37 -02:30
@Override
public String call () throws Exception
{
return getCrashInformation ();
}
2012-08-21 18:48:12 -04:00
@Override
public String getLabel ()
{
return "FML" ;
}
2012-07-23 22:50:37 -02:30
};
}
public List < ModContainer > getActiveModList ()
{
2012-10-18 14:41:32 -04:00
return modController != null ? modController . getActiveModList () : ImmutableList . < ModContainer > of ();
2012-07-23 22:50:37 -02:30
}
public ModState getModState ( ModContainer selectedMod )
{
return modController . getModState ( selectedMod );
}
2012-07-30 22:31:07 -04:00
public String getMCVersionString ()
{
return "Minecraft " + mccversion ;
}
2012-08-02 02:08:30 -02:30
2013-01-22 09:11:44 -05:00
public boolean serverStarting ( Object server )
2012-08-02 02:08:30 -02:30
{
2013-01-22 09:11:44 -05:00
try
{
modController . distributeStateMessage ( LoaderState . SERVER_STARTING , server );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . SERVER_STARTING , false );
2013-01-22 09:11:44 -05:00
}
catch ( Throwable t )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , t , "A fatal exception occurred during the server starting event" );
2013-01-22 09:11:44 -05:00
return false ;
}
return true ;
2012-08-02 02:08:30 -02:30
}
2012-08-04 11:26:51 -04:00
2012-08-02 02:08:30 -02:30
public void serverStarted ()
{
modController . distributeStateMessage ( LoaderState . SERVER_STARTED );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . SERVER_STARTED , false );
2012-08-02 02:08:30 -02:30
}
2012-08-04 11:26:51 -04:00
2012-08-02 02:08:30 -02:30
public void serverStopping ()
{
modController . distributeStateMessage ( LoaderState . SERVER_STOPPING );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . SERVER_STOPPING , false );
2012-08-02 02:08:30 -02:30
}
2012-08-11 02:43:04 -04:00
public BiMap < ModContainer , Object > getModObjectList ()
{
return modController . getModObjectList ();
}
public BiMap < Object , ModContainer > getReversedModObjectList ()
{
return getModObjectList (). inverse ();
}
2012-08-13 15:26:29 -04:00
public ModContainer activeModContainer ()
{
2012-12-07 01:52:16 -05:00
return modController != null ? modController . activeContainer () : null ;
2012-08-13 15:26:29 -04:00
}
public boolean isInState ( LoaderState state )
{
return modController . isInState ( state );
}
2012-10-02 01:29:46 -04:00
public MinecraftDummyContainer getMinecraftModContainer ()
{
return minecraft ;
}
2012-10-24 16:39:55 -04:00
2013-08-28 10:18:28 -07:00
public boolean hasReachedState ( LoaderState state )
{
return modController != null ? modController . hasReachedState ( state ) : false ;
}
2012-10-25 09:02:49 -04:00
2013-08-28 10:18:28 -07:00
public String getMCPVersionString ()
{
return String . format ( "MCP v%s" , mcpversion );
}
2012-12-30 23:52:38 -05:00
public void serverStopped ()
{
2013-12-24 00:47:10 -05:00
GameData . revertToFrozen ();
2012-12-30 23:52:38 -05:00
modController . distributeStateMessage ( LoaderState . SERVER_STOPPED );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . SERVER_STOPPED , true );
modController . transition ( LoaderState . AVAILABLE , true );
2012-12-30 23:52:38 -05:00
}
2013-01-22 09:11:44 -05:00
2013-01-24 10:23:36 -05:00
public boolean serverAboutToStart ( Object server )
2013-01-22 09:11:44 -05:00
{
try
{
modController . distributeStateMessage ( LoaderState . SERVER_ABOUT_TO_START , server );
2013-06-11 14:41:19 -04:00
modController . transition ( LoaderState . SERVER_ABOUT_TO_START , false );
2013-01-22 09:11:44 -05:00
}
catch ( Throwable t )
{
2013-12-16 11:47:48 -05:00
FMLLog . log ( Level . ERROR , t , "A fatal exception occurred during the server about to start event" );
2013-01-22 09:11:44 -05:00
return false ;
}
return true ;
}
2013-03-23 15:39:34 -04:00
2013-03-30 12:12:54 -04:00
public Map < String , String > getFMLBrandingProperties ()
{
if ( fmlBrandingProperties == null )
{
Properties loaded = new Properties ();
try
{
loaded . load ( getClass (). getClassLoader (). getResourceAsStream ( "fmlbranding.properties" ));
}
2013-03-31 10:01:46 -04:00
catch ( Exception e )
2013-03-30 12:12:54 -04:00
{
// File not found - ignore
}
fmlBrandingProperties = Maps . fromProperties ( loaded );
}
return fmlBrandingProperties ;
}
2013-06-27 08:38:11 -04:00
public Map < String , String > getCustomModProperties ( String modId )
{
return getIndexedModList (). get ( modId ). getCustomModProperties ();
}
2013-12-09 01:22:24 -05: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 ();
}
}
FMLLog . info ( "Attempting connection with missing mods %s at %s" , difference , side );
return true ;
}
2013-12-10 19:25:49 -05:00
2014-02-08 12:18:34 -05:00
public List < String > fireMissingMappingEvent ( ArrayListMultimap < String , String > missing , boolean isLocalWorld )
2013-12-18 08:34:20 -05:00
{
if ( ! missing . isEmpty ())
{
FMLLog . fine ( "There are %d mappings missing - attempting a mod remap" , missing . size ());
ArrayListMultimap < String , MissingMapping > missingMappings = ArrayListMultimap . create ();
List < MissingMapping > remaps = Lists . newArrayList ();
for ( Map . Entry < String , String > mapping : missing . entries ())
{
MissingMapping m = new MissingMapping ( mapping . getValue (), remaps );
missingMappings . put ( mapping . getKey (), m );
}
FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent ( missingMappings );
modController . propogateStateMessage ( missingEvent );
2014-02-08 12:18:34 -05:00
if ( ! missingMappings . isEmpty () && isLocalWorld )
{
FMLLog . severe ( "There are unidentified mappings in this world - we are going to attempt to process anyway" );
for ( java . util . Map . Entry < String , MissingMapping > missed : missingMappings . entries ())
{
remaps . add ( missed . getValue ());
}
}
else if ( ! missingMappings . isEmpty () && ! isLocalWorld )
2013-12-18 08:34:20 -05:00
{
2014-02-08 12:18:34 -05:00
List < String > missedMapping = Lists . newArrayList ();
for ( java . util . Map . Entry < String , MissingMapping > missed : missingMappings . entries ())
{
missedMapping . add ( missed . getKey () + ":" + missed . getValue (). name );
}
return ImmutableList . copyOf ( missedMapping );
2013-12-18 08:34:20 -05:00
}
2014-02-08 12:18:34 -05:00
return GameData . processIdRematches ( remaps , isLocalWorld );
2013-12-18 08:34:20 -05:00
}
2014-02-08 12:18:34 -05:00
return ImmutableList . of ();
2013-12-18 08:34:20 -05:00
}
2013-12-10 19:25:49 -05:00
public void fireRemapEvent ( Map < String , Integer []> remaps )
{
if ( remaps . isEmpty ())
{
2013-12-16 11:47:48 -05:00
FMLLog . finer ( "Skipping remap event - no remaps occured" );
2013-12-10 19:25:49 -05:00
}
else
{
modController . propogateStateMessage ( new FMLModIdMappingEvent ( remaps ));
}
}
2013-12-17 10:51:00 -05: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" );
}
}
2012-03-28 16:44:36 -04:00
}