2012-03-30 14:11:13 +00:00
|
|
|
/*
|
2012-03-30 15:35:04 +00:00
|
|
|
* The FML Forge Mod Loader suite.
|
2012-03-30 14:11:13 +00:00
|
|
|
* Copyright (C) 2012 cpw
|
2012-04-05 14:07:52 +00:00
|
|
|
*
|
2012-03-30 14:11:13 +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; either version 2.1 of the License, or any later version.
|
2012-04-05 14:07:52 +00:00
|
|
|
*
|
2012-03-30 14:11:13 +00:00
|
|
|
* 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.
|
2012-04-05 14:07:52 +00:00
|
|
|
*
|
2012-03-30 14:11:13 +00:00
|
|
|
* 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-04-02 22:09:06 +00:00
|
|
|
package cpw.mods.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;
|
2012-03-28 20:44:36 +00:00
|
|
|
import java.io.IOException;
|
2012-08-14 17:36:29 +00:00
|
|
|
import java.util.Comparator;
|
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;
|
2012-07-24 01:20:37 +00:00
|
|
|
import java.util.concurrent.Callable;
|
2012-03-30 05:12:59 +00:00
|
|
|
import java.util.logging.Level;
|
2012-03-28 20:44:36 +00:00
|
|
|
|
2012-08-13 04:17:34 +00:00
|
|
|
import net.minecraft.src.CallableMinecraftVersion;
|
2012-08-02 04:38:30 +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;
|
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;
|
2012-07-22 14:26:38 +00:00
|
|
|
import com.google.common.collect.Lists;
|
|
|
|
import com.google.common.collect.Maps;
|
2012-08-23 17:43:25 +00:00
|
|
|
import com.google.common.collect.Sets;
|
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;
|
2012-08-23 17:43:25 +00:00
|
|
|
import com.google.common.collect.Sets.SetView;
|
2012-08-14 17:36:29 +00:00
|
|
|
import com.google.common.collect.TreeMultimap;
|
2012-07-22 14:26:38 +00:00
|
|
|
|
2012-07-24 01:20:37 +00:00
|
|
|
import cpw.mods.fml.common.LoaderState.ModState;
|
2012-07-22 14:26:38 +00:00
|
|
|
import cpw.mods.fml.common.discovery.ModDiscoverer;
|
2012-08-04 15:26:51 +00:00
|
|
|
import cpw.mods.fml.common.event.FMLLoadEvent;
|
2012-07-22 14:26:38 +00:00
|
|
|
import cpw.mods.fml.common.functions.ModIdFunction;
|
2012-08-17 13:24:38 +00:00
|
|
|
import cpw.mods.fml.common.modloader.BaseModProxy;
|
2012-04-03 16:03:21 +00:00
|
|
|
import cpw.mods.fml.common.toposort.ModSorter;
|
2012-07-14 17:58:46 +00:00
|
|
|
import cpw.mods.fml.common.toposort.ModSortingException;
|
2012-04-05 20:22:47 +00:00
|
|
|
import cpw.mods.fml.common.toposort.TopologicalSort;
|
2012-07-30 21:01:27 +00:00
|
|
|
import cpw.mods.fml.common.versioning.ArtifactVersion;
|
|
|
|
import cpw.mods.fml.common.versioning.VersionParser;
|
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
|
|
|
*/
|
2012-04-05 14:07:52 +00:00
|
|
|
public class Loader
|
|
|
|
{
|
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;
|
|
|
|
private static String mcsversion;
|
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;
|
2012-04-05 20:22:47 +00:00
|
|
|
/**
|
|
|
|
* The canonical configuration directory
|
|
|
|
*/
|
2012-04-05 14:07:52 +00:00
|
|
|
private File canonicalConfigDir;
|
2012-04-05 20:22:47 +00:00
|
|
|
/**
|
|
|
|
* The canonical minecraft directory
|
|
|
|
*/
|
2012-04-05 14:07:52 +00:00
|
|
|
private File canonicalMinecraftDir;
|
2012-07-14 17:58:46 +00:00
|
|
|
/**
|
|
|
|
* The captured error
|
|
|
|
*/
|
2012-06-08 19:31:30 +00:00
|
|
|
private Exception capturedError;
|
2012-07-22 14:26:38 +00:00
|
|
|
private File canonicalModsDir;
|
|
|
|
private LoadController modController;
|
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;
|
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
|
|
|
|
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];
|
|
|
|
mcsversion = (String) data[5];
|
|
|
|
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()
|
|
|
|
{
|
2012-07-14 17:58:46 +00:00
|
|
|
modClassLoader = new ModClassLoader(getClass().getClassLoader());
|
2012-08-13 04:17:34 +00:00
|
|
|
String actualMCVersion = new CallableMinecraftVersion(null).func_71493_a();
|
|
|
|
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-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()
|
|
|
|
{
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("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();
|
2012-08-23 18:33:54 +00:00
|
|
|
for (ModContainer mod : getActiveModList())
|
2012-04-05 14:07:52 +00:00
|
|
|
{
|
2012-07-30 21:01:27 +00:00
|
|
|
modVersions.put(mod.getModId(), mod.getProcessedVersion());
|
|
|
|
}
|
|
|
|
|
2012-08-23 18:33:54 +00:00
|
|
|
for (ModContainer mod : getActiveModList())
|
2012-07-30 21:01:27 +00:00
|
|
|
{
|
2012-08-23 17:43:25 +00:00
|
|
|
Set<ArtifactVersion> missingMods = Sets.difference(mod.getRequirements(), modVersions.values());
|
|
|
|
if (!missingMods.isEmpty())
|
|
|
|
{
|
|
|
|
FMLLog.severe("The mod %s (%s) requires mods %s to be available", mod.getModId(), mod.getName(), missingMods);
|
|
|
|
throw new MissingModsException(missingMods);
|
|
|
|
}
|
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-08-23 17:43:25 +00:00
|
|
|
missingMods = Sets.newHashSet();
|
2012-07-30 21:01:27 +00:00
|
|
|
if (modVersions.containsKey(v.getLabel()))
|
|
|
|
{
|
|
|
|
if (!v.containsVersion(modVersions.get(v.getLabel())))
|
|
|
|
{
|
2012-08-23 17:43:25 +00:00
|
|
|
missingMods.add(v);
|
2012-07-30 21:01:27 +00:00
|
|
|
}
|
|
|
|
}
|
2012-07-14 17:58:46 +00:00
|
|
|
}
|
2012-08-23 17:43:25 +00:00
|
|
|
if (!missingMods.isEmpty())
|
|
|
|
{
|
|
|
|
FMLLog.severe("The mod %s (%s) requires mod versions %s to be available", mod.getModId(), mod.getName(), missingMods);
|
|
|
|
throw new MissingModsException(missingMods);
|
|
|
|
}
|
2012-04-05 14:07:52 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("All mod requirements are satisfied");
|
|
|
|
|
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
|
|
|
|
{
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("Sorting mods into an ordered list");
|
2012-07-14 17:58:46 +00:00
|
|
|
mods = sorter.sort();
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("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");
|
|
|
|
FMLLog.severe("The visited mod list is %s", sortException.getExceptionData().getVisitedNodes());
|
|
|
|
FMLLog.severe("The first mod in the cycle is %s", sortException.getExceptionData().getFirstBadNode());
|
|
|
|
FMLLog.log(Level.SEVERE, sortException, "The full error");
|
2012-07-14 17:58:46 +00:00
|
|
|
throw new LoaderException(sortException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally
|
2012-04-05 14:07:52 +00:00
|
|
|
{
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("Mod sorting data:");
|
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());
|
2012-08-04 15:26:51 +00:00
|
|
|
}
|
2012-07-23 19:03:17 +00:00
|
|
|
}
|
|
|
|
if (mods.size()==0)
|
2012-07-30 02:54:59 +00:00
|
|
|
{
|
|
|
|
FMLLog.fine("No 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
|
|
|
* This is visited during first initialization by Minecraft to scan and load
|
|
|
|
* the mods from all sources 1. The minecraft jar itself (for loading of in
|
|
|
|
* jar mods- I would like to remove this if possible but forge depends on it
|
|
|
|
* at present) 2. The mods directory with expanded subdirs, searching for
|
|
|
|
* mods named mod_*.class 3. The mods directory for zip and jar files,
|
|
|
|
* searching for mod classes named mod_*.class again
|
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
|
|
|
|
* "FML mods", which currently is effectively a NO-OP. If they are
|
2012-08-07 02:30:13 +00:00
|
|
|
* determined to be {@link BaseModProxy} subclasses they are loaded as such.
|
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
|
|
|
*/
|
2012-08-06 13:52:42 +00:00
|
|
|
private ModDiscoverer identifyMods()
|
2012-04-05 14:07:52 +00:00
|
|
|
{
|
2012-08-04 15:26:51 +00:00
|
|
|
FMLLog.fine("Building injected Mod Containers %s", injectedContainers);
|
2012-08-10 14:03:33 +00:00
|
|
|
File coremod = new File(minecraftDir,"coremods");
|
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)
|
|
|
|
{
|
|
|
|
FMLLog.log(Level.SEVERE, e, "A problem occured instantiating the injected mod container %s", cont);
|
|
|
|
throw new LoaderException(e);
|
|
|
|
}
|
2012-08-10 14:03:33 +00:00
|
|
|
mods.add(new InjectedModContainer(mc,coremod));
|
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
|
|
|
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.info("Searching %s for mods", canonicalModsDir.getAbsolutePath());
|
2012-07-22 14:26:38 +00:00
|
|
|
discoverer.findModDirMods(canonicalModsDir);
|
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)
|
|
|
|
{
|
|
|
|
boolean foundDupe = false;
|
|
|
|
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());
|
|
|
|
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()));
|
|
|
|
foundDupe = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (foundDupe) { throw new LoaderException(); }
|
|
|
|
}
|
|
|
|
|
2012-04-05 20:22:47 +00:00
|
|
|
/**
|
2012-07-22 14:26:38 +00:00
|
|
|
* @return
|
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
|
|
|
|
{
|
|
|
|
canonicalMinecraftDir = minecraftDir.getCanonicalFile();
|
|
|
|
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)
|
|
|
|
{
|
2012-07-31 02:31:07 +00:00
|
|
|
FMLLog.log(Level.SEVERE, ioe, "Failed to resolve loader directories: mods : %s ; config %s", canonicalModsDir.getAbsolutePath(),
|
|
|
|
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);
|
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("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
|
|
|
}
|
|
|
|
}
|
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-07-22 14:26:38 +00:00
|
|
|
return ImmutableList.copyOf(instance().mods);
|
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
|
|
|
* Called from the hook to start mod loading. We trigger the
|
|
|
|
* {@link #identifyMods()} and {@link #preModInit()} phases here. Finally,
|
|
|
|
* the mod list is frozen completely and is consider immutable from then on.
|
2012-04-05 20:22:47 +00:00
|
|
|
*/
|
2012-04-05 14:07:52 +00:00
|
|
|
public void loadMods()
|
|
|
|
{
|
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);
|
2012-08-02 04:38:30 +00:00
|
|
|
modController.transition(LoaderState.LOADING);
|
2012-08-06 13:52:42 +00:00
|
|
|
ModDiscoverer disc = identifyMods();
|
2012-07-22 14:26:38 +00:00
|
|
|
disableRequestedMods();
|
2012-08-23 18:33:54 +00:00
|
|
|
modController.distributeStateMessage(FMLLoadEvent.class);
|
2012-04-05 14:07:52 +00:00
|
|
|
sortModList();
|
2012-07-22 14:26:38 +00:00
|
|
|
mods = ImmutableList.copyOf(mods);
|
2012-08-02 04:38:30 +00:00
|
|
|
modController.transition(LoaderState.CONSTRUCTING);
|
2012-08-06 13:52:42 +00:00
|
|
|
modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, disc.getASMTable());
|
2012-07-23 19:03:17 +00:00
|
|
|
modController.transition(LoaderState.PREINITIALIZATION);
|
2012-08-10 21:42:43 +00:00
|
|
|
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, disc.getASMTable(), canonicalConfigDir);
|
2012-07-23 19:03:17 +00:00
|
|
|
modController.transition(LoaderState.INITIALIZATION);
|
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-04 16:35:46 +00:00
|
|
|
String disabledModList = System.getProperty("fml.modStates", "");
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("Received a system property request \'%s\'",disabledModList);
|
2012-07-22 14:26:38 +00:00
|
|
|
Map<String, String> sysPropertyStateList = Splitter.on(CharMatcher.anyOf(";:"))
|
|
|
|
.omitEmptyStrings().trimResults().withKeyValueSeparator("=")
|
|
|
|
.split(disabledModList);
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("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
|
|
|
|
2012-07-22 14:26:38 +00:00
|
|
|
File disabledModFile = new File(canonicalConfigDir, "fmlModState.properties");
|
|
|
|
Properties disabledModListProperties = new Properties();
|
|
|
|
if (disabledModFile.exists() && disabledModFile.isFile())
|
|
|
|
{
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("Found a mod state file %s", disabledModFile.getName());
|
2012-07-22 14:26:38 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
disabledModListProperties.load(new FileReader(disabledModFile));
|
2012-07-30 02:54:59 +00:00
|
|
|
FMLLog.fine("Loaded states for %d mods from file", disabledModListProperties.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
|
|
|
}
|
|
|
|
}
|
|
|
|
modStates.putAll(Maps.fromProperties(disabledModListProperties));
|
|
|
|
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>()
|
|
|
|
{
|
|
|
|
public Boolean apply(String input)
|
|
|
|
{
|
|
|
|
return !Boolean.parseBoolean(input);
|
|
|
|
}
|
|
|
|
});
|
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
|
|
|
|
* @return
|
|
|
|
*/
|
2012-04-05 14:07:52 +00:00
|
|
|
public static boolean isModLoaded(String modname)
|
|
|
|
{
|
|
|
|
return instance().namedMods.containsKey(modname);
|
2012-03-28 20:44:36 +00:00
|
|
|
}
|
2012-04-05 20:22:47 +00:00
|
|
|
|
2012-04-05 14:07:52 +00:00
|
|
|
/**
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
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()
|
|
|
|
{
|
2012-07-22 14:26:38 +00:00
|
|
|
StringBuilder ret = new StringBuilder();
|
2012-07-24 01:20:37 +00:00
|
|
|
List<String> branding = FMLCommonHandler.instance().getBrandings();
|
2012-07-30 02:54:59 +00:00
|
|
|
|
2012-07-24 01:20:37 +00:00
|
|
|
Joiner.on(' ').skipNulls().appendTo(ret, branding.subList(1, branding.size()));
|
2012-07-23 19:03:17 +00:00
|
|
|
if (modController!=null)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
/**
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public String getFMLVersionString()
|
|
|
|
{
|
|
|
|
return String.format("FML v%s.%s.%s.%s", major, minor, rev, build);
|
|
|
|
}
|
2012-05-15 19:52:19 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public ClassLoader getModClassLoader()
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2012-07-22 14:26:38 +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)
|
|
|
|
{
|
|
|
|
parseFailure=true;
|
|
|
|
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 *
|
|
|
|
if (targetIsAll && target.length()>1)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
|
|
|
parseFailure=true;
|
|
|
|
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
|
|
|
|
if (targetIsAll && target.indexOf('@')>-1)
|
|
|
|
{
|
|
|
|
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))
|
|
|
|
{
|
2012-07-30 21:01:27 +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
|
|
|
|
{
|
|
|
|
parseFailure=true;
|
|
|
|
}
|
|
|
|
}
|
2012-07-30 02:54:59 +00:00
|
|
|
|
2012-07-22 14:26:38 +00:00
|
|
|
if (parseFailure)
|
|
|
|
{
|
2012-07-23 19:03:17 +00:00
|
|
|
FMLLog.log(Level.WARNING, "Unable to parse dependency string %s", dependencyString);
|
2012-07-22 14:26:38 +00:00
|
|
|
throw new LoaderException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Map<String,ModContainer> getIndexedModList()
|
|
|
|
{
|
|
|
|
return ImmutableMap.copyOf(namedMods);
|
|
|
|
}
|
2012-07-23 19:03:17 +00:00
|
|
|
|
|
|
|
public void initializeMods()
|
|
|
|
{
|
|
|
|
// Mod controller should be in the initialization state here
|
2012-08-02 04:38:30 +00:00
|
|
|
modController.distributeStateMessage(LoaderState.INITIALIZATION);
|
2012-07-23 19:03:17 +00:00
|
|
|
modController.transition(LoaderState.POSTINITIALIZATION);
|
2012-08-02 04:38:30 +00:00
|
|
|
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
|
|
|
|
modController.transition(LoaderState.AVAILABLE);
|
|
|
|
modController.distributeStateMessage(LoaderState.AVAILABLE);
|
2012-07-23 19:03:17 +00:00
|
|
|
FMLLog.info("Forge Mod Loader has successfully loaded %d mod%s", mods.size(), mods.size()==1 ? "" : "s");
|
|
|
|
}
|
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()
|
|
|
|
{
|
|
|
|
return modController.getActiveModList();
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
public void serverStarting(Object server)
|
|
|
|
{
|
|
|
|
modController.distributeStateMessage(LoaderState.SERVER_STARTING, server);
|
|
|
|
modController.transition(LoaderState.SERVER_STARTING);
|
|
|
|
}
|
2012-08-04 15:26:51 +00:00
|
|
|
|
2012-08-02 04:38:30 +00:00
|
|
|
public void serverStarted()
|
|
|
|
{
|
|
|
|
modController.distributeStateMessage(LoaderState.SERVER_STARTED);
|
|
|
|
modController.transition(LoaderState.SERVER_STARTED);
|
|
|
|
}
|
2012-08-04 15:26:51 +00:00
|
|
|
|
2012-08-02 04:38:30 +00:00
|
|
|
public void serverStopping()
|
|
|
|
{
|
|
|
|
modController.distributeStateMessage(LoaderState.SERVER_STOPPING);
|
|
|
|
modController.transition(LoaderState.SERVER_STOPPING);
|
|
|
|
modController.transition(LoaderState.AVAILABLE);
|
2012-08-04 15:26:51 +00:00
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
return modController.activeContainer();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isInState(LoaderState state)
|
|
|
|
{
|
|
|
|
return modController.isInState(state);
|
|
|
|
}
|
2012-03-28 20:44:36 +00:00
|
|
|
}
|