Code to disable mods at runtime, or other times.

This commit is contained in:
Christian committed 2013-12-17 10:51:00 -05:00
1 parent 95f0447881
commit 751c5e39a4
8 files changed
+120 -24

No files matched your search

@@ -141,12 +141,6 @@ public class DummyModContainer implements ModContainer
return false;
}
@Override
public boolean isNetworkMod()
{
return false;
}
@Override
public String getDisplayVersion()
{
@@ -186,4 +180,10 @@ public class DummyModContainer implements ModContainer
{
return null;
}
@Override
public Disableable canBeDisabled()
{
return Disableable.NEVER;
}
}
@@ -54,6 +54,7 @@ import cpw.mods.fml.common.event.FMLEvent;
import cpw.mods.fml.common.event.FMLFingerprintViolationEvent;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent;
import cpw.mods.fml.common.event.FMLModDisabledEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent;
@@ -80,7 +81,6 @@ public class FMLModContainer implements ModContainer
private EventBus eventBus;
private LoadController controller;
private DefaultArtifactVersion processedVersion;
private boolean isNetworkMod;
private String annotationDependencies;
private VersionRange minecraftAccepted;
@@ -89,6 +89,7 @@ public class FMLModContainer implements ModContainer
private Certificate certificate;
private String modLanguage;
private ILanguageAdapter languageAdapter;
private Disableable disableability;
private ListMultimap<Class<? extends FMLEvent>,Method> eventMethods;
private Map<String, String> customModProperties;
private ModCandidate candidate;
@@ -103,6 +104,7 @@ public class FMLModContainer implements ModContainer
.add(FMLServerStoppedEvent.class)
.add(IMCEvent.class)
.add(FMLFingerprintViolationEvent.class)
.add(FMLModDisabledEvent.class)
.build();
public FMLModContainer(String className, ModCandidate container, Map<String,Object> modDescriptor)
@@ -491,7 +493,16 @@ public class FMLModContainer implements ModContainer
customModProperties = EMPTY_PROPERTIES;
}
Boolean hasDisableableFlag = (Boolean) descriptor.get("canBeDeactivated");
boolean hasReverseDepends = !event.getReverseDependencies().get(getModId()).isEmpty();
if (hasDisableableFlag != null && hasDisableableFlag)
{
disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.YES;
}
else
{
disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.RESTART;
}
Method factoryMethod = gatherAnnotations(clazz);
modInstance = getLanguageAdapter().getNewInstance(this,clazz, modClassLoader, factoryMethod);
NetworkRegistry.INSTANCE.register(this, clazz, (String)(descriptor.containsKey("acceptableRemoteVersions") ? descriptor.get("acceptableRemoteVersions") : null), event.getASMHarvestedData());
@@ -544,12 +555,6 @@ public class FMLModContainer implements ModContainer
return false;
}
@Override
public boolean isNetworkMod()
{
return isNetworkMod;
}
@Override
public String getDisplayVersion()
{
@@ -605,4 +610,10 @@ public class FMLModContainer implements ModContainer
descriptor.put("description", modMetadata.description);
return descriptor;
}
@Override
public Disableable canBeDisabled()
{
return disableability;
}
}
@@ -109,11 +109,6 @@ public class InjectedModContainer implements ModContainer
return wrappedContainer.getProcessedVersion();
}
@Override
public boolean isNetworkMod()
{
return wrappedContainer.isNetworkMod();
}
@Override
public boolean isImmutable()
{
@@ -173,4 +168,10 @@ public class InjectedModContainer implements ModContainer
{
return wrappedContainer.getSharedModDescriptor();
}
@Override
public Disableable canBeDisabled()
{
return wrappedContainer.canBeDisabled();
}
}
@@ -14,6 +14,7 @@ package cpw.mods.fml.common;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
@@ -28,6 +29,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
@@ -35,6 +37,7 @@ import com.google.common.eventbus.Subscribe;
import cpw.mods.fml.common.LoaderState.ModState;
import cpw.mods.fml.common.event.FMLEvent;
import cpw.mods.fml.common.event.FMLLoadEvent;
import cpw.mods.fml.common.event.FMLModDisabledEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLStateEvent;
import cpw.mods.fml.common.functions.ArtifactVersionNameFunction;
@@ -63,6 +66,20 @@ public class LoadController
}
void disableMod(ModContainer mod)
{
HashMap<String, EventBus> temporary = Maps.newHashMap(eventChannels);
String modId = mod.getModId();
EventBus bus = temporary.remove(modId);
bus.post(new FMLModDisabledEvent());
if (errors.get(modId).isEmpty())
{
eventChannels = ImmutableMap.copyOf(temporary);
modStates.put(modId, ModState.DISABLED);
modObjectList.remove(mod);
activeModList.remove(mod);
}
}
@Subscribe
public void buildModList(FMLLoadEvent event)
{
@@ -14,6 +14,7 @@ package cpw.mods.fml.common;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Comparator;
@@ -29,14 +30,17 @@ import com.google.common.base.CharMatcher;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets;
import com.google.common.collect.Ordering;
@@ -45,6 +49,7 @@ import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;
import cpw.mods.fml.common.LoaderState.ModState;
import cpw.mods.fml.common.ModContainer.Disableable;
import cpw.mods.fml.common.discovery.ModDiscoverer;
import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLLoadEvent;
@@ -121,6 +126,10 @@ public class Loader
* A named list of mods
*/
private Map<String, ModContainer> namedMods;
/**
* A reverse dependency graph for mods
*/
private ListMultimap<String, String> reverseDependencies;
/**
* The canonical configuration directory
*/
@@ -133,6 +142,7 @@ public class Loader
private static File minecraftDir;
private static List<String> injectedContainers;
private ImmutableMap<String, String> fmlBrandingProperties;
private File forcedModFile;
public static Loader instance()
{
@@ -187,6 +197,7 @@ public class Loader
modVersions.put(mod.getModId(), mod.getProcessedVersion());
}
ArrayListMultimap<String, String> reqList = ArrayListMultimap.create();
for (ModContainer mod : getActiveModList())
{
if (!mod.acceptableMinecraftVersionRange().containsVersion(minecraft.getProcessedVersion()))
@@ -196,6 +207,7 @@ public class Loader
}
Map<String,ArtifactVersion> names = Maps.uniqueIndex(mod.getRequirements(), new ArtifactVersionNameFunction());
Set<ArtifactVersion> versionMissingMods = Sets.newHashSet();
Set<String> missingMods = Sets.difference(names.keySet(), modVersions.keySet());
if (!missingMods.isEmpty())
{
@@ -206,6 +218,7 @@ public class Loader
}
throw new MissingModsException(versionMissingMods);
}
reqList.putAll(mod.getModId(), names.keySet());
ImmutableList<ArtifactVersion> allDeps = ImmutableList.<ArtifactVersion>builder().addAll(mod.getDependants()).addAll(mod.getDependencies()).build();
for (ArtifactVersion v : allDeps)
{
@@ -226,6 +239,7 @@ public class Loader
FMLLog.finer("All mod requirements are satisfied");
reverseDependencies = Multimaps.invertFrom(reqList, ArrayListMultimap.<String,String>create());
ModSorter sorter = new ModSorter(getActiveModList(), namedMods);
try
@@ -468,7 +482,7 @@ public class Loader
}
}
modController.transition(LoaderState.CONSTRUCTING, false);
modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, disc.getASMTable());
modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, disc.getASMTable(), reverseDependencies);
FMLLog.fine("Mod signature data");
for (ModContainer mod : getActiveModList())
{
@@ -493,7 +507,7 @@ public class Loader
FMLLog.finer("System property request managing the state of %d mods", sysPropertyStateList.size());
Map<String, String> modStates = Maps.newHashMap();
File forcedModFile = new File(canonicalConfigDir, "fmlModState.properties");
forcedModFile = new File(canonicalConfigDir, "fmlModState.properties");
Properties forcedModListProperties = new Properties();
if (forcedModFile.exists() && forcedModFile.isFile())
{
@@ -837,4 +851,40 @@ public class Loader
modController.propogateStateMessage(new FMLModIdMappingEvent(remaps));
}
}
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");
}
}
}
@@ -39,6 +39,9 @@ import cpw.mods.fml.common.versioning.VersionRange;
public interface ModContainer
{
public static enum Disableable {
YES, RESTART, NEVER, DEPENDENCIES;
}
/**
* The globally unique modid for this mod
*/
@@ -126,8 +129,6 @@ public interface ModContainer
boolean isImmutable();
boolean isNetworkMod();
String getDisplayVersion();
VersionRange acceptableMinecraftVersionRange();
@@ -140,4 +141,6 @@ public interface ModContainer
public Class<?> getCustomResourcePackClass();
Map<String, String> getSharedModDescriptor();
Disableable canBeDisabled();
}
@@ -5,13 +5,15 @@
* 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
*
*
* Contributors:
* cpw - implementation
*/
package cpw.mods.fml.common.event;
import com.google.common.collect.ListMultimap;
import cpw.mods.fml.common.LoaderState.ModState;
import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.common.discovery.ASMDataTable;
@@ -20,11 +22,14 @@ public class FMLConstructionEvent extends FMLStateEvent
{
private ModClassLoader modClassLoader;
private ASMDataTable asmData;
private ListMultimap<String,String> reverseDependencies;
@SuppressWarnings("unchecked")
public FMLConstructionEvent(Object... eventData)
{
this.modClassLoader = (ModClassLoader)eventData[0];
this.asmData = (ASMDataTable) eventData[1];
this.reverseDependencies = (ListMultimap<String, String>) eventData[2];
}
public ModClassLoader getModClassLoader()
@@ -42,4 +47,9 @@ public class FMLConstructionEvent extends FMLStateEvent
{
return asmData;
}
public ListMultimap<String, String> getReverseDependencies()
{
return reverseDependencies;
}
}
@@ -0,0 +1,4 @@
package cpw.mods.fml.common.event;
public class FMLModDisabledEvent extends FMLEvent {
}