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"); } }
public void errorOccurred(ModContainer modContainer, Throwable exception) { if (exception instanceof InvocationTargetException) { errors.put(modContainer.getModId(), ((InvocationTargetException) exception).getCause()); } else { errors.put(modContainer.getModId(), exception); } }
private void parseSimpleFieldAnnotation( SetMultimap<String, ASMData> annotations, String annotationClassName, Function<ModContainer, Object> retreiver) throws IllegalAccessException { String[] annName = annotationClassName.split("\\."); String annotationName = annName[annName.length - 1]; for (ASMData targets : annotations.get(annotationClassName)) { String targetMod = (String) targets.getAnnotationInfo().get("value"); Field f = null; Object injectedMod = null; ModContainer mc = this; boolean isStatic = false; Class<?> clz = modInstance.getClass(); if (!Strings.isNullOrEmpty(targetMod)) { if (Loader.isModLoaded(targetMod)) { mc = Loader.instance().getIndexedModList().get(targetMod); } else { mc = null; } } if (mc != null) { try { clz = Class.forName(targets.getClassName(), true, Loader.instance().getModClassLoader()); f = clz.getDeclaredField(targets.getObjectName()); f.setAccessible(true); isStatic = Modifier.isStatic(f.getModifiers()); injectedMod = retreiver.apply(mc); } catch (Exception e) { Throwables.propagateIfPossible(e); FMLLog.log( getModId(), Level.WARNING, e, "Attempting to load @%s in class %s for %s and failing", annotationName, targets.getClassName(), mc.getModId()); } } if (f != null) { Object target = null; if (!isStatic) { target = modInstance; if (!modInstance.getClass().equals(clz)) { FMLLog.log( getModId(), Level.WARNING, "Unable to inject @%s in non-static field %s.%s for %s as it is NOT the primary mod instance", annotationName, targets.getClassName(), targets.getObjectName(), mc.getModId()); continue; } } f.set(target, injectedMod); } } }
/** * Called from the furnace to lookup fuel values * * @param itemId * @param itemDamage * @return */ public int fuelLookup(int itemId, int itemDamage) { int fv = 0; for (ModContainer mod : Loader.getModList()) { fv = Math.max(fv, mod.lookupFuelValue(itemId, itemDamage)); } return fv; }
public static void inject( ModContainer mod, ASMDataTable data, Side side, ILanguageAdapter languageAdapter) { FMLLog.fine("Attempting to inject @SidedProxy classes into %s", mod.getModId()); Set<ASMData> targets = data.getAnnotationsFor(mod).get(SidedProxy.class.getName()); ClassLoader mcl = Loader.instance().getModClassLoader(); for (ASMData targ : targets) { try { Class<?> proxyTarget = Class.forName(targ.getClassName(), true, mcl); Field target = proxyTarget.getDeclaredField(targ.getObjectName()); if (target == null) { // Impossible? FMLLog.severe( "Attempted to load a proxy type into %s.%s but the field was not found", targ.getClassName(), targ.getObjectName()); throw new LoaderException(); } SidedProxy annotation = target.getAnnotation(SidedProxy.class); if (!Strings.isNullOrEmpty(annotation.modId()) && !annotation.modId().equals(mod.getModId())) { FMLLog.fine( "Skipping proxy injection for %s.%s since it is not for mod %s", targ.getClassName(), targ.getObjectName(), mod.getModId()); continue; } String targetType = side.isClient() ? annotation.clientSide() : annotation.serverSide(); Object proxy = Class.forName(targetType, true, mcl).newInstance(); if (languageAdapter.supportsStatics() && (target.getModifiers() & Modifier.STATIC) == 0) { FMLLog.severe( "Attempted to load a proxy type %s into %s.%s, but the field is not static", targetType, targ.getClassName(), targ.getObjectName()); throw new LoaderException(); } if (!target.getType().isAssignableFrom(proxy.getClass())) { FMLLog.severe( "Attempted to load a proxy type %s into %s.%s, but the types don't match", targetType, targ.getClassName(), targ.getObjectName()); throw new LoaderException(); } languageAdapter.setProxy(target, proxyTarget, proxy); } catch (Exception e) { FMLLog.log( Level.SEVERE, e, "An error occured trying to load a proxy into %s.%s", targ.getAnnotationInfo(), targ.getClassName(), targ.getObjectName()); throw new LoaderException(e); } } // Allow language specific proxy injection. languageAdapter.setInternalProxies(mod, side, mcl); }
public List<IKeyHandler> gatherKeyBindings() { List<IKeyHandler> allKeys = new ArrayList<IKeyHandler>(); for (ModContainer mod : Loader.getModList()) { allKeys.addAll(mod.getKeys()); } for (ModContainer mod : auxilliaryContainers) { allKeys.addAll(mod.getKeys()); } return allKeys; }
public void handleWorldDataSave( SaveHandler handler, WorldInfo worldInfo, NBTTagCompound tagCompound) { for (ModContainer mc : Loader.instance().getModList()) { if (mc instanceof InjectedModContainer) { WorldAccessContainer wac = ((InjectedModContainer) mc).getWrappedWorldAccessContainer(); if (wac != null) { NBTTagCompound dataForWriting = wac.getDataForWriting(handler, worldInfo); tagCompound.func_74766_a(mc.getModId(), dataForWriting); } } } }
public void printModStates(StringBuilder ret) { for (ModContainer mc : loader.getModList()) { ret.append("\n\t") .append(mc.getModId()) .append(" [") .append(mc.getName()) .append("] (") .append(mc.getSource().getName()) .append(") "); Joiner.on("->").appendTo(ret, modStates.get(mc.getModId())); } }
@Override public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info) { NBTTagCompound fmlData = new NBTTagCompound(); NBTTagList list = new NBTTagList(); for (ModContainer mc : Loader.instance().getActiveModList()) { NBTTagCompound mod = new NBTTagCompound(); mod.setString("ModId", mc.getModId()); mod.setString("ModVersion", mc.getVersion()); list.appendTag(mod); } fmlData.setTag("ModList", list); NBTTagList itemList = new NBTTagList(); GameData.writeItemData(itemList); fmlData.setTag("ModItemData", itemList); return fmlData; }
/** * The primary loading code * * <p>The found resources are first loaded into the {@link #modClassLoader} (always) then scanned * for class resources matching the specification above. * * <p>If they provide the {@link Mod} annotation, they will be loaded as "FML mods" * * <p>Finally, if they are successfully loaded as classes, they are then added to the available * mod list. */ private ModDiscoverer identifyMods() { FMLLog.fine("Building injected Mod Containers %s", injectedContainers); // Add in the MCP mod container mods.add(new InjectedModContainer(mcp, new File("minecraft.jar"))); for (String cont : injectedContainers) { ModContainer mc; try { mc = (ModContainer) Class.forName(cont, true, modClassLoader).newInstance(); } catch (Exception e) { FMLLog.log( Level.ERROR, e, "A problem occured instantiating the injected mod container %s", cont); throw new LoaderException(e); } mods.add(new InjectedModContainer(mc, mc.getSource())); } ModDiscoverer discoverer = new ModDiscoverer(); FMLLog.fine( "Attempting to load mods contained in the minecraft jar file and associated classes"); discoverer.findClasspathMods(modClassLoader); FMLLog.fine("Minecraft jar mods loaded successfully"); FMLLog.getLogger() .log( Level.INFO, "Found {} mods from the command line. Injecting into mod discoverer", ModListHelper.additionalMods.size()); FMLLog.info("Searching %s for mods", canonicalModsDir.getAbsolutePath()); discoverer.findModDirMods( canonicalModsDir, ModListHelper.additionalMods.values().toArray(new File[0])); File versionSpecificModsDir = new File(canonicalModsDir, mccversion); if (versionSpecificModsDir.isDirectory()) { FMLLog.info("Also searching %s for mods", versionSpecificModsDir); discoverer.findModDirMods(versionSpecificModsDir); } mods.addAll(discoverer.identifyMods()); identifyDuplicates(mods); namedMods = Maps.uniqueIndex(mods, new ModIdFunction()); FMLLog.info( "Forge Mod Loader has identified %d mod%s to load", mods.size(), mods.size() != 1 ? "s" : ""); return discoverer; }
public void handleWorldDataLoad( SaveHandler handler, WorldInfo worldInfo, NBTTagCompound tagCompound) { if (getEffectiveSide() != Side.SERVER) { return; } if (handlerSet.contains(handler)) { return; } handlerSet.add(handler); Map<String, NBTBase> additionalProperties = Maps.newHashMap(); worldInfo.setAdditionalProperties(additionalProperties); for (ModContainer mc : Loader.instance().getModList()) { if (mc instanceof InjectedModContainer) { WorldAccessContainer wac = ((InjectedModContainer) mc).getWrappedWorldAccessContainer(); if (wac != null) { wac.readData( handler, worldInfo, additionalProperties, tagCompound.func_74775_l(mc.getModId())); } } } }
@Subscribe public void propogateStateMessage(FMLEvent stateEvent) { if (stateEvent instanceof FMLPreInitializationEvent) { modObjectList = buildModObjectList(); } for (ModContainer mc : activeModList) { activeContainer = mc; String modId = mc.getModId(); stateEvent.applyModContainer(activeContainer()); FMLLog.log( modId, Level.FINEST, "Sending event %s to mod %s", stateEvent.getEventType(), modId); eventChannels.get(modId).post(stateEvent); FMLLog.log(modId, Level.FINEST, "Sent event %s to mod %s", stateEvent.getEventType(), modId); activeContainer = null; if (stateEvent instanceof FMLStateEvent) { if (!errors.containsKey(modId)) { modStates.put(modId, ((FMLStateEvent) stateEvent).getModState()); } else { modStates.put(modId, ModState.ERRORED); } } } }
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()); SetMultimap<ModContainer, File> dupes = LinkedHashMultimap.create(); 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())); dupes.putAll(e.getElement(), dupsearch.get(e.getElement())); } } if (!dupes.isEmpty()) { throw new DuplicateModsFoundException(dupes); } }
@Override public void readData( SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag) { if (tag.hasKey("ModList")) { NBTTagList modList = tag.getTagList("ModList"); for (int i = 0; i < modList.tagCount(); i++) { NBTTagCompound mod = (NBTTagCompound) modList.tagAt(i); String modId = mod.getString("ModId"); String modVersion = mod.getString("ModVersion"); ModContainer container = Loader.instance().getIndexedModList().get(modId); if (container == null) { FMLLog.log( "fml.ModTracker", Level.SEVERE, "This world was saved with mod %s which appears to be missing, things may not work well", modId); continue; } if (!modVersion.equals(container.getVersion())) { FMLLog.log( "fml.ModTracker", Level.INFO, "This world was saved with mod %s version %s and it is now at version %s, things may not work well", modId, modVersion, container.getVersion()); } } } if (tag.hasKey("ModItemData")) { NBTTagList modList = tag.getTagList("ModItemData"); Set<ItemData> worldSaveItems = GameData.buildWorldItemData(modList); GameData.validateWorldSave(worldSaveItems); } else { GameData.validateWorldSave(null); } }
public ImmutableBiMap<ModContainer, Object> buildModObjectList() { ImmutableBiMap.Builder<ModContainer, Object> builder = ImmutableBiMap.<ModContainer, Object>builder(); for (ModContainer mc : activeModList) { if (!mc.isImmutable() && mc.getMod() != null) { builder.put(mc, mc.getMod()); } if (mc.getMod() == null && !mc.isImmutable() && state != LoaderState.CONSTRUCTING) { FMLLog.severe( "There is a severe problem with %s - it appears not to have constructed correctly", mc.getModId()); if (state != LoaderState.CONSTRUCTING) { this.errorOccurred(mc, new RuntimeException()); } } } return builder.build(); }
/** @param mod */ public void injectSidedProxyDelegate(ModContainer mod) { ProxyInjector injector = mod.findSidedProxy(); if (injector != null) { injector.inject(mod, sidedDelegate.getSide()); } }
/** * Called from the hook to start mod loading. We trigger the {@link #identifyMods()} and * Constructing, Preinitalization, and Initalization phases here. Finally, the mod list is frozen * completely and is consider immutable from then on. */ public void loadMods() { progressBar = ProgressManager.push("Loading", 7); progressBar.step("Constructing Mods"); initializeLoader(); mods = Lists.newArrayList(); namedMods = Maps.newHashMap(); modController = new LoadController(this); modController.transition(LoaderState.LOADING, false); discoverer = identifyMods(); ModAPIManager.INSTANCE.manageAPI(modClassLoader, discoverer); disableRequestedMods(); modController.distributeStateMessage(FMLLoadEvent.class); sortModList(); ModAPIManager.INSTANCE.cleanupAPIContainers(modController.getActiveModList()); ModAPIManager.INSTANCE.cleanupAPIContainers(mods); mods = ImmutableList.copyOf(mods); for (File nonMod : discoverer.getNonModLibs()) { if (nonMod.isFile()) { 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()); try { modClassLoader.addFile(nonMod); } catch (MalformedURLException e) { FMLLog.log( Level.ERROR, e, "Encountered a weird problem with non-mod file injection : %s", nonMod.getName()); } } } modController.transition(LoaderState.CONSTRUCTING, false); modController.distributeStateMessage( LoaderState.CONSTRUCTING, modClassLoader, discoverer.getASMTable(), reverseDependencies); 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()); } }); FMLLog.fine("Mod signature data"); 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:"); for (ModContainer mod : getActiveModList()) { if (mod.getSigningCertificate() == null) FMLLog.fine( "\t\t%s\t(%s\t%s)\t%s", mod.getModId(), mod.getName(), mod.getVersion(), mod.getSource().getName()); } if (getActiveModList().isEmpty()) { FMLLog.fine("No user mod signature data found"); } progressBar.step("Initializing mods Phase 1"); modController.transition(LoaderState.PREINITIALIZATION, false); }
@Override public int compare(ModContainer o1, ModContainer o2) { return o1.getModId().compareTo(o2.getModId()); }
/** * 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. */ private void sortModList() { FMLLog.finer("Verifying mod requirements are satisfied"); try { BiMap<String, ArtifactVersion> modVersions = HashBiMap.create(); for (ModContainer mod : Iterables.concat(getActiveModList(), ModAPIManager.INSTANCE.getAPIList())) { modVersions.put(mod.getModId(), mod.getProcessedVersion()); } ArrayListMultimap<String, String> reqList = ArrayListMultimap.create(); for (ModContainer mod : getActiveModList()) { 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); } 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()) { FMLLog.severe( "The mod %s (%s) requires mods %s to be available", mod.getModId(), mod.getName(), missingMods); for (String modid : missingMods) { versionMissingMods.add(names.get(modid)); } throw new MissingModsException(versionMissingMods, mod.getModId(), mod.getName()); } reqList.putAll(mod.getModId(), names.keySet()); ImmutableList<ArtifactVersion> allDeps = ImmutableList.<ArtifactVersion>builder() .addAll(mod.getDependants()) .addAll(mod.getDependencies()) .build(); for (ArtifactVersion v : allDeps) { if (modVersions.containsKey(v.getLabel())) { if (!v.containsVersion(modVersions.get(v.getLabel()))) { versionMissingMods.add(v); } } } if (!versionMissingMods.isEmpty()) { FMLLog.severe( "The mod %s (%s) requires mod versions %s to be available", mod.getModId(), mod.getName(), versionMissingMods); throw new MissingModsException(versionMissingMods, mod.getModId(), mod.getName()); } } FMLLog.finer("All mod requirements are satisfied"); reverseDependencies = Multimaps.invertFrom(reqList, ArrayListMultimap.<String, String>create()); ModSorter sorter = new ModSorter(getActiveModList(), namedMods); try { FMLLog.finer("Sorting mods into an ordered list"); List<ModContainer> sortedMods = sorter.sort(); // Reset active list to the sorted list modController.getActiveModList().clear(); modController.getActiveModList().addAll(sortedMods); // And inject the sorted list into the overall list mods.removeAll(sortedMods); sortedMods.addAll(mods); mods = sortedMods; FMLLog.finer("Mod sorting completed successfully"); } catch (ModSortingException sortException) { FMLLog.severe( "A dependency cycle was detected in the input mod set so an ordering cannot be determined"); 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()); } FMLLog.log(Level.ERROR, sortException, "The full error"); throw sortException; } } finally { FMLLog.fine("Mod sorting data"); int unprintedMods = mods.size(); for (ModContainer mod : getActiveModList()) { if (!mod.isImmutable()) { FMLLog.fine( "\t%s(%s:%s): %s (%s)", mod.getModId(), mod.getName(), mod.getVersion(), mod.getSource().getName(), mod.getSortingRules()); unprintedMods--; } } if (unprintedMods == mods.size()) { FMLLog.fine("No user mods found to sort"); } } }
public ModState getModState(ModContainer selectedMod) { return Iterables.getLast(modStates.get(selectedMod.getModId()), ModState.AVAILABLE); }
/** @param mod */ public void loadMetadataFor(ModContainer mod) { if (mod.getSourceType() == SourceType.JAR) { try { ZipFile jar = new ZipFile(mod.getSource()); ZipEntry infoFile = jar.getEntry("mcmod.info"); if (infoFile != null) { InputStream input = jar.getInputStream(infoFile); ModMetadata data = sidedDelegate.readMetadataFrom(input, mod); mod.setMetadata(data); } else { getFMLLogger() .fine( String.format( "Failed to find mcmod.info file in %s for %s", mod.getSource().getName(), mod.getName())); } } catch (Exception e) { // Something wrong but we don't care getFMLLogger() .fine( String.format( "Failed to find mcmod.info file in %s for %s", mod.getSource().getName(), mod.getName())); getFMLLogger().throwing("FMLCommonHandler", "loadMetadataFor", e); } } else { try { InputStream input = Loader.instance().getModClassLoader().getResourceAsStream(mod.getName() + ".info"); if (input == null) { input = Loader.instance() .getModClassLoader() .getResourceAsStream("net/minecraft/src/" + mod.getName() + ".info"); } if (input != null) { ModMetadata data = sidedDelegate.readMetadataFrom(input, mod); mod.setMetadata(data); } } catch (Exception e) { // Something wrong but we don't care getFMLLogger() .fine( String.format( "Failed to find %s.info file in %s for %s", mod.getName(), mod.getSource().getName(), mod.getName())); getFMLLogger().throwing("FMLCommonHandler", "loadMetadataFor", e); } } }
@Subscribe public void buildModList(FMLLoadEvent event) { this.modList = loader.getIndexedModList(); Builder<String, EventBus> eventBus = ImmutableMap.builder(); for (ModContainer mod : loader.getModList()) { EventBus bus = new EventBus(mod.getModId()); boolean isActive = mod.registerBus(bus, this); if (isActive) { Level level = Logger.getLogger(mod.getModId()).getLevel(); FMLLog.log( mod.getModId(), Level.FINE, "Mod Logging channel %s configured at %s level.", level == null ? "default" : level); FMLLog.log(mod.getModId(), Level.INFO, "Activating mod %s", mod.getModId()); activeModList.add(mod); modStates.put(mod.getModId(), ModState.UNLOADED); eventBus.put(mod.getModId(), bus); } else { FMLLog.log( mod.getModId(), Level.WARNING, "Mod %s has been disabled through configuration", mod.getModId()); modStates.put(mod.getModId(), ModState.UNLOADED); modStates.put(mod.getModId(), ModState.DISABLED); } } eventChannels = eventBus.build(); }