public void transition(LoaderState desiredState) { LoaderState oldState = state; state = state.transition(!errors.isEmpty()); if (state != desiredState) { Throwable toThrow = null; FMLLog.severe( "Fatal errors were detected during the transition from %s to %s. Loading cannot continue", oldState, desiredState); StringBuilder sb = new StringBuilder(); printModStates(sb); FMLLog.getLogger().severe(sb.toString()); if (errors.size() > 0) { FMLLog.severe("The following problems were captured during this phase"); for (Entry<String, Throwable> error : errors.entries()) { FMLLog.log(Level.SEVERE, error.getValue(), "Caught exception from %s", error.getKey()); if (error.getValue() instanceof IFMLHandledException) { toThrow = error.getValue(); } else if (toThrow == null) { toThrow = error.getValue(); } } } else { FMLLog.severe( "The ForgeModLoader state engine has become corrupted. Probably, a state was missed by and invalid modification to a base class" + "ForgeModLoader depends on. This is a critical error and not recoverable. Investigate any modifications to base classes outside of" + "ForgeModLoader, especially Optifine, to see if there are fixes available."); throw new RuntimeException("The ForgeModLoader state engine is invalid"); } if (toThrow != null && toThrow instanceof RuntimeException) { throw (RuntimeException) toThrow; } else { throw new LoaderException(toThrow); } } }
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); }
private void initializeLoader() { 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(); canonicalModsDir = modsDir.getCanonicalFile(); } catch (IOException ioe) { FMLLog.log( Level.ERROR, ioe, "Failed to resolve loader directories: mods : %s ; config %s", canonicalModsDir.getAbsolutePath(), configDir.getAbsolutePath()); throw new LoaderException(ioe); } if (!canonicalModsDir.exists()) { FMLLog.info("No mod directory found, creating one: %s", canonicalModsPath); boolean dirMade = canonicalModsDir.mkdir(); if (!dirMade) { FMLLog.severe("Unable to create the mod directory %s", canonicalModsPath); throw new LoaderException( String.format("Unable to create the mod directory %s", canonicalModsPath)); } FMLLog.info("Mod directory created successfully"); } if (!canonicalConfigDir.exists()) { FMLLog.fine("No config directory found, creating one: %s", canonicalConfigPath); boolean dirMade = canonicalConfigDir.mkdir(); if (!dirMade) { FMLLog.severe("Unable to create the config directory %s", canonicalConfigPath); throw new LoaderException(); } FMLLog.info("Config directory created successfully"); } if (!canonicalModsDir.isDirectory()) { FMLLog.severe("Attempting to load mods from %s, which is not a directory", canonicalModsPath); throw new LoaderException(); } if (!configDir.isDirectory()) { FMLLog.severe( "Attempting to load configuration from %s, which is not a directory", canonicalConfigPath); throw new LoaderException(); } readInjectedDependencies(); }
public BiMap<ModContainer, Object> getModObjectList() { if (modObjectList == null) { FMLLog.severe( "Detected an attempt by a mod %s to perform game activity during mod construction. This is a serious programming error.", activeContainer); return buildModObjectList(); } return ImmutableBiMap.copyOf(modObjectList); }
private Loader() { modClassLoader = new ModClassLoader(getClass().getClassLoader()); if (!mccversion.equals(MC_VERSION)) { FMLLog.severe( "This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, MC_VERSION); throw new LoaderException( String.format( "This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, MC_VERSION)); } minecraft = new MinecraftDummyContainer(MC_VERSION); mcp = new MCPDummyContainer( MetadataCollection.from(getClass().getResourceAsStream("/mcpmod.info"), "MCP") .getMetadataForId("mcp", 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(); }
private Multimap<Class<? extends Annotation>, Object> gatherAnnotations(Class<?> clazz) throws Exception { Multimap<Class<? extends Annotation>, Object> anns = ArrayListMultimap.create(); for (Method m : clazz.getDeclaredMethods()) { for (Annotation a : m.getAnnotations()) { if (modTypeAnnotations.containsKey(a.annotationType())) { Class<?>[] paramTypes = new Class[] {modTypeAnnotations.get(a.annotationType())}; if (Arrays.equals(m.getParameterTypes(), paramTypes)) { m.setAccessible(true); anns.put(a.annotationType(), m); } else { FMLLog.severe( "The mod %s appears to have an invalid method annotation %s. This annotation can only apply to methods with argument types %s -it will not be called", getModId(), a.annotationType().getSimpleName(), Arrays.toString(paramTypes)); } } } } return anns; }
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); } }
/** * Fire a FMLMissingMappingsEvent to let mods determine how blocks/items defined in the world * save, but missing from the runtime, are to be handled. * * @param missing Map containing missing names with their associated id, blocks need to come * before items for remapping. * @param isLocalWorld Whether this is executing for a world load (local/server) or a client. * @param gameData GameData instance where the new map's config is to be loaded into. * @return List with the mapping results. */ public List<String> fireMissingMappingEvent( LinkedHashMap<String, Integer> missingBlocks, LinkedHashMap<String, Integer> missingItems, boolean isLocalWorld, GameData gameData, Map<String, Integer[]> remapBlocks, Map<String, Integer[]> remapItems) { if (missingBlocks.isEmpty() && missingItems.isEmpty()) // nothing to do { return ImmutableList.of(); } FMLLog.fine( "There are %d mappings missing - attempting a mod remap", missingBlocks.size() + missingItems.size()); ArrayListMultimap<String, MissingMapping> missingMappings = ArrayListMultimap.create(); for (Map.Entry<String, Integer> mapping : missingBlocks.entrySet()) { MissingMapping m = new MissingMapping(GameRegistry.Type.BLOCK, mapping.getKey(), mapping.getValue()); missingMappings.put(m.name.substring(0, m.name.indexOf(':')), m); } for (Map.Entry<String, Integer> mapping : missingItems.entrySet()) { MissingMapping m = new MissingMapping(GameRegistry.Type.ITEM, mapping.getKey(), mapping.getValue()); missingMappings.put(m.name.substring(0, m.name.indexOf(':')), m); } FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent(missingMappings); modController.propogateStateMessage(missingEvent); if (isLocalWorld) // local world, warn about entries still being set to the default action { boolean didWarn = false; for (MissingMapping mapping : missingMappings.values()) { if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT) { if (!didWarn) { FMLLog.severe( "There are unidentified mappings in this world - we are going to attempt to process anyway"); didWarn = true; } FMLLog.severe( "Unidentified %s: %s, id %d", mapping.type == Type.BLOCK ? "block" : "item", mapping.name, mapping.id); } } } else // remote world, fail on entries with the default action { List<String> missedMapping = new ArrayList<String>(); for (MissingMapping mapping : missingMappings.values()) { if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT) { missedMapping.add(mapping.name); } } if (!missedMapping.isEmpty()) { return ImmutableList.copyOf(missedMapping); } } return GameData.processIdRematches( missingMappings.values(), isLocalWorld, gameData, remapBlocks, remapItems); }
/** * 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"); } } }