diff --git a/mods.toml b/mods.toml new file mode 100644 index 000000000..0b77473fa --- /dev/null +++ b/mods.toml @@ -0,0 +1,62 @@ +# The name of the mod loader type to load +modLoader="javafml" +# A version range to match for said mod loader +loaderVersion="[2.0,)" +# A URL to query for updates +updateJSONURL="http://myurl.me/" +# A URL to refer people to when problems occur with this mod +issueTrackerURL="http://my.issue.tracker/" +# Extra mod loader property +randomScalaProperty=fishy +# Arbitrary key-value property pairs available to mods +[properties] +key="value" + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] + # The modid of the mod + modId="inventorysorter" + # The version number of the mod + version="1.1" + # A display name for the mod + displayName="Inventory Sorter" + # The description text for the mod (multi line!) + description=''' + This is my mod, there may be + others, but this one is mine + ''' + # A random extra property for a mod loader + randomExtraProperty=somevalue + # Arbitrary key-value pairs + [inventorysorter.properties] + key="value" + # A list of dependencies + [[inventorysorter.dependencies]] + # the modid of the dependency + modid="forge" + # Does this dependency have to exist - if not, ordering below must be specified + mandatory=true + # The version range of the dependency + versionRange="[14.23.2.0,)" + # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory + ordering=NONE + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side=BOTH + # Here's another dependency + [[inventorysorter.dependencies]] + modid="minecraft" + mandatory=true + versionRange="[1.12.2]" + ordering=NONE + side=BOTH +# Here's another mod in this jar +[[mods]] + # Note that other mod types may want to add additional key-value pairs in here + modId="inventorysortertoo" + # ${jarVersion} will read the Implementation-Version of the jar file + version="${jarVersion}" + displayName="Inventory Sorter as well" + description=''' + This is also my mod, there may be + others, but this one is mine + ''' \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/gui/NotificationModUpdateScreen.java b/src/main/java/net/minecraftforge/client/gui/NotificationModUpdateScreen.java index a5d768a79..4fad1dc0e 100644 --- a/src/main/java/net/minecraftforge/client/gui/NotificationModUpdateScreen.java +++ b/src/main/java/net/minecraftforge/client/gui/NotificationModUpdateScreen.java @@ -28,6 +28,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.ForgeVersion.Status; +import net.minecraftforge.fml.client.ClientModLoader; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.relauncher.Side; @@ -55,16 +56,7 @@ public class NotificationModUpdateScreen extends GuiScreen { if (modButton != null) { - for (ModContainer mod : Loader.instance().getModList()) - { - Status status = ForgeVersion.getResult(mod).status; - if (status == Status.OUTDATED || status == Status.BETA_OUTDATED) - { - // TODO: Needs better visualization, maybe stacked icons - // drawn in a terrace-like pattern? - showNotification = Status.OUTDATED; - } - } + showNotification = ClientModLoader.checkForUpdates(); } hasCheckedForUpdates = true; } diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index 799540867..e4eb2b871 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -67,23 +67,24 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.resources.IResourceManager; import net.minecraft.item.Item; -import net.minecraft.launchwrapper.Launch; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.registry.IRegistry; import net.minecraftforge.client.model.animation.AnimationItemOverrideList; import net.minecraftforge.client.model.animation.ModelBlockAnimation; +import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.model.IModelState; import net.minecraftforge.common.model.Models; import net.minecraftforge.common.model.TRSRTransformation; import net.minecraftforge.common.model.animation.IClip; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; +import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.ProgressManager; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; -import net.minecraftforge.fml.common.registry.ForgeRegistries; +import net.minecraftforge.logging.ModelLoaderErrorMessage; import net.minecraftforge.registries.IRegistryDelegate; import org.apache.commons.lang3.tuple.Pair; @@ -105,13 +106,15 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import static net.minecraftforge.fml.Logging.MODELLOADING; +import static net.minecraftforge.fml.Logging.fmlLog; + public final class ModelLoader extends ModelBakery { private final Map stateModels = Maps.newHashMap(); @@ -128,9 +131,6 @@ public final class ModelLoader extends ModelBakery return isLoading; } - private final boolean enableVerboseMissingInfo = (Boolean)Launch.blackboard.get("fml.deobfuscatedEnvironment") || Boolean.parseBoolean(System.getProperty("forge.verboseMissingModelLogging", "false")); - private final int verboseMissingInfoCount = Integer.parseInt(System.getProperty("forge.verboseMissingModelLoggingCount", "5")); - public ModelLoader(IResourceManager manager, TextureMap map, BlockModelShapes shapes) { super(manager, map, shapes); @@ -915,7 +915,7 @@ public final class ModelLoader extends ModelBakery } } - private static class ItemLoadingException extends ModelLoaderRegistry.LoaderException + public static class ItemLoadingException extends ModelLoaderRegistry.LoaderException { private final Exception normalException; private final Exception blockstateException; @@ -933,93 +933,15 @@ public final class ModelLoader extends ModelBakery */ public void onPostBakeEvent(IRegistry modelRegistry) { - if (!isLoading) return; - IBakedModel missingModel = modelRegistry.getObject(MODEL_MISSING); - Map modelErrors = Maps.newHashMap(); - Set printedBlockStateErrors = Sets.newHashSet(); - Multimap reverseBlockMap = null; - Multimap reverseItemMap = HashMultimap.create(); - if(enableVerboseMissingInfo) - { - reverseBlockMap = HashMultimap.create(); - for(Map.Entry entry : blockModelShapes.getBlockStateMapper().putAllStateModelLocations().entrySet()) - { - reverseBlockMap.put(entry.getValue(), entry.getKey()); - } - ForgeRegistries.ITEMS.forEach(item -> - { - for(String s : getVariantNames(item)) - { - ModelResourceLocation memory = getInventoryVariant(s); - reverseItemMap.put(memory, item.getRegistryName().toString()); - } - }); - } - for(Map.Entry entry : loadingExceptions.entrySet()) { // ignoring pure ResourceLocation arguments, all things we care about pass ModelResourceLocation if(entry.getKey() instanceof ModelResourceLocation) { - ModelResourceLocation location = (ModelResourceLocation)entry.getKey(); - IBakedModel model = modelRegistry.getObject(location); - if(model == null || model == missingModel || model instanceof FancyMissingModel.BakedModel) - { - String domain = entry.getKey().getResourceDomain(); - Integer errorCountBox = modelErrors.get(domain); - int errorCount = errorCountBox == null ? 0 : errorCountBox; - errorCount++; - if(errorCount < verboseMissingInfoCount) - { - String errorMsg = "Exception loading model for variant " + entry.getKey(); - if(enableVerboseMissingInfo) - { - Collection blocks = reverseBlockMap.get(location); - if(!blocks.isEmpty()) - { - if(blocks.size() == 1) - { - errorMsg += " for blockstate \"" + blocks.iterator().next() + "\""; - } - else - { - errorMsg += " for blockstates [\"" + Joiner.on("\", \"").join(blocks) + "\"]"; - } - } - Collection items = reverseItemMap.get(location); - if(!items.isEmpty()) - { - if(!blocks.isEmpty()) errorMsg += " and"; - if(items.size() == 1) - { - errorMsg += " for item \"" + items.iterator().next() + "\""; - } - else - { - errorMsg += " for items [\"" + Joiner.on("\", \"").join(items) + "\"]"; - } - } - } - if(entry.getValue() instanceof ItemLoadingException) - { - ItemLoadingException ex = (ItemLoadingException)entry.getValue(); - FMLLog.log.error("{}, normal location exception: ", errorMsg, ex.normalException); - FMLLog.log.error("{}, blockstate location exception: ", errorMsg, ex.blockstateException); - } - else - { - FMLLog.log.error(errorMsg, entry.getValue()); - } - ResourceLocation blockstateLocation = new ResourceLocation(location.getResourceDomain(), location.getResourcePath()); - if(loadingExceptions.containsKey(blockstateLocation) && !printedBlockStateErrors.contains(blockstateLocation)) - { - FMLLog.log.error("Exception loading blockstate for the variant {}: ", location, loadingExceptions.get(blockstateLocation)); - printedBlockStateErrors.add(blockstateLocation); - } - } - modelErrors.put(domain, errorCount); - } + fmlLog.debug(MODELLOADING, ()-> new ModelLoaderErrorMessage((ModelResourceLocation)entry.getKey(), entry.getValue(), modelRegistry, this.blockModelShapes, this::getVariantNames)); + final ModelResourceLocation location = (ModelResourceLocation)entry.getKey(); + final IBakedModel model = modelRegistry.getObject(location); if(model == null) { modelRegistry.putObject(location, missingModel); @@ -1031,28 +953,13 @@ public final class ModelLoader extends ModelBakery IBakedModel model = modelRegistry.getObject(missing); if(model == null || model == missingModel) { - String domain = missing.getResourceDomain(); - Integer errorCountBox = modelErrors.get(domain); - int errorCount = errorCountBox == null ? 0 : errorCountBox; - errorCount++; - if(errorCount < verboseMissingInfoCount) - { - FMLLog.log.fatal("Model definition for location {} not found", missing); - } - modelErrors.put(domain, errorCount); + fmlLog.debug(MODELLOADING, ()-> new ModelLoaderErrorMessage(missing, null, modelRegistry, this.blockModelShapes, this::getVariantNames)); } if(model == null) { modelRegistry.putObject(missing, missingModel); } } - for(Map.Entry e : modelErrors.entrySet()) - { - if(e.getValue() >= verboseMissingInfoCount) - { - FMLLog.log.fatal("Suppressed additional {} model loading errors for domain {}", e.getValue() - verboseMissingInfoCount, e.getKey()); - } - } loadingExceptions.clear(); missingVariants.clear(); isLoading = false; diff --git a/src/main/java/net/minecraftforge/common/ForgeVersion.java b/src/main/java/net/minecraftforge/common/ForgeVersion.java index 364914dcd..a0ded6e82 100644 --- a/src/main/java/net/minecraftforge/common/ForgeVersion.java +++ b/src/main/java/net/minecraftforge/common/ForgeVersion.java @@ -23,10 +23,15 @@ import static net.minecraftforge.common.ForgeVersion.Status.*; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -35,6 +40,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import com.electronwill.nightconfig.core.Config; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import com.electronwill.nightconfig.core.path.PathConfig; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -57,7 +68,7 @@ public class ForgeVersion //This number is incremented every minecraft release, never reset public static final int minorVersion = 23; //This number is incremented every time a interface changes or new major feature is added, and reset every Minecraft version - public static final int revisionVersion = 4; + public static final int revisionVersion = 2; //This number is incremented every time Jenkins builds Forge, and never reset. Should always be 0 in the repo code. public static final int buildVersion = 0; // This is the minecraft version we're building for - used in various places in Forge/FML code @@ -69,7 +80,7 @@ public class ForgeVersion @SuppressWarnings("unused") private static String target = null; - private static final Logger log = LogManager.getLogger(MOD_ID + ".VersionCheck"); + private static final Logger log = LogManager.getLogger("ForgeVersionCheck"); private static final int MAX_HTTP_REDIRECTS = Integer.getInteger("http.maxRedirects", 20); @@ -110,6 +121,27 @@ public class ForgeVersion return String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion); } + public static List getModInfos() + { + PathConfig minecraftmod; + PathConfig forgemod; + try + { + minecraftmod = PathConfig.of(Paths.get(ForgeVersion.class.getClassLoader().getResource("minecraftmod.toml").toURI())); + forgemod = PathConfig.of(Paths.get(ForgeVersion.class.getClassLoader().getResource("forgemod.toml").toURI())); + minecraftmod.load(); + forgemod.load(); + } + catch (URISyntaxException | NullPointerException e) + { + throw new RuntimeException("Missing toml configs for minecraft and forge!", e); + } + return Arrays.asList( + new ModInfo(null, minecraftmod), + new ModInfo(null, forgemod) + ); + } + public static enum Status { PENDING(), diff --git a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java index aa65e13d8..632500b83 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java +++ b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import net.minecraftforge.fml.ModThreadContext; import net.minecraftforge.fml.common.LoaderState; import org.apache.logging.log4j.Level; @@ -177,9 +178,7 @@ public abstract class FluidRegistry private static String uniqueName(Fluid fluid) { - ModContainer activeModContainer = Loader.instance().activeModContainer(); - String activeModContainerName = activeModContainer == null ? "minecraft" : activeModContainer.getModId(); - return activeModContainerName+":"+fluid.getName(); + return ModThreadContext.get().getCurrentContainer().getPrefix() +":"+fluid.getName(); } /** diff --git a/src/main/java/net/minecraftforge/fml/CrashReportExtender.java b/src/main/java/net/minecraftforge/fml/CrashReportExtender.java new file mode 100644 index 000000000..d912519a5 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/CrashReportExtender.java @@ -0,0 +1,85 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraftforge.fml.common.ICrashCallable; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.relauncher.CoreModManager; + +import java.util.ArrayList; +import java.util.List; + +public class CrashReportExtender +{ + private static List crashCallables = new ArrayList<>(); + + static { + registerCrashCallable(new ICrashCallable() { + @Override + public String call() + { + return "New FML!"; + } + + @Override + public String getLabel() + { + return "FML"; + } + }); + + registerCrashCallable(new ICrashCallable() + { + @Override + public String call() + { + return "Nothing"; + } + + @Override + public String getLabel() + { + return "Loaded coremods (and transformers)"; + } + }); + + } + + public static void enhanceCrashReport(final CrashReport crashReport, final CrashReportCategory category) + { + for (final ICrashCallable call: crashCallables) + { + category.addDetail(call.getLabel(), call); + } + } + + public static void registerCrashCallable(ICrashCallable callable) + { + crashCallables.add(callable); + } + + public static void addCrashReportHeader(StringBuilder stringbuilder, CrashReport crashReport) + { + } +} diff --git a/src/main/java/net/minecraftforge/fml/DefaultModContainers.java b/src/main/java/net/minecraftforge/fml/DefaultModContainers.java new file mode 100644 index 000000000..b909469a8 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/DefaultModContainers.java @@ -0,0 +1,32 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.fml.common.MinecraftDummyContainer; +import net.minecraftforge.fml.common.ModContainer; + +public class DefaultModContainers +{ + // no construction + private DefaultModContainers() {} + + public static final ModContainer MINECRAFT = new MinecraftDummyContainer(ForgeVersion.mcVersion); +} diff --git a/src/main/java/net/minecraftforge/fml/FMLEnvironment.java b/src/main/java/net/minecraftforge/fml/FMLEnvironment.java new file mode 100644 index 000000000..b5f788856 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/FMLEnvironment.java @@ -0,0 +1,28 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import net.minecraftforge.api.Side; +import net.minecraftforge.fml.loading.FMLLoader; + +public class FMLEnvironment +{ + public static final Side side = FMLLoader.getSide(); +} diff --git a/src/main/java/net/minecraftforge/fml/Java9BackportUtils.java b/src/main/java/net/minecraftforge/fml/Java9BackportUtils.java new file mode 100644 index 000000000..ad4e03381 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/Java9BackportUtils.java @@ -0,0 +1,43 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; + +public class Java9BackportUtils +{ + public static + Collector flatMapping(Function> mapper, + Collector downstream) { + BiConsumer downstreamAccumulator = downstream.accumulator(); + return Collector.of(downstream.supplier(), + (r, t) -> { + try (Stream result = mapper.apply(t)) { + if (result != null) + result.sequential().forEach(u -> downstreamAccumulator.accept(r, u)); + } + }, + downstream.combiner(), downstream.finisher(), + downstream.characteristics().toArray(new Collector.Characteristics[0])); + } +} diff --git a/src/main/java/net/minecraftforge/fml/LaunchTesting.java b/src/main/java/net/minecraftforge/fml/LaunchTesting.java index 0dc90b9a6..dba8dd0b3 100644 --- a/src/main/java/net/minecraftforge/fml/LaunchTesting.java +++ b/src/main/java/net/minecraftforge/fml/LaunchTesting.java @@ -21,19 +21,45 @@ package net.minecraftforge.fml; import com.google.common.base.Strings; import cpw.mods.modlauncher.Launcher; +import net.minecraft.block.Block; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.item.EnumDyeColor; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; +import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; +import org.apache.logging.log4j.core.filter.MarkerFilter; import java.io.File; import java.lang.reflect.Field; +import static cpw.mods.modlauncher.Logging.CLASSLOADING; +import static net.minecraftforge.fml.Logging.fmlLog; + public class LaunchTesting { public static void main(String... args) throws InterruptedException { Configurator.setRootLevel(Level.DEBUG); + final MarkerFilter classloadingFilter = MarkerFilter.createFilter("CLASSLOADING", Filter.Result.DENY, Filter.Result.NEUTRAL); + final MarkerFilter launchpluginFilter = MarkerFilter.createFilter("LAUNCHPLUGIN", Filter.Result.DENY, Filter.Result.NEUTRAL); + final LoggerContext logcontext = LoggerContext.getContext(false); +// logcontext.getConfiguration().addFilter(classloadingFilter); + logcontext.getConfiguration().addFilter(launchpluginFilter); + logcontext.updateLoggers(); hackNatives(); - Launcher.main("--launchTarget", "devfml","--gameDir", "projects/run", "--accessToken", "blah", "--version", "FMLDev"); + Launcher.main("--launchTarget", "devfmlclient","--gameDir", "projects/run", + "--accessToken", "blah", "--version", "FMLDev", "--assetIndex", "1.12", + "--assetsDir","/home/cpw/.gradle/caches/minecraft/assets", + "--userProperties", "{}"); Thread.sleep(10000); } diff --git a/src/main/java/net/minecraftforge/fml/Logging.java b/src/main/java/net/minecraftforge/fml/Logging.java index de7234ccd..3c835d361 100644 --- a/src/main/java/net/minecraftforge/fml/Logging.java +++ b/src/main/java/net/minecraftforge/fml/Logging.java @@ -29,7 +29,11 @@ import org.apache.logging.log4j.core.config.Configurator; public class Logging { public static final Logger fmlLog = LogManager.getLogger("FML"); + + // Lots of markers public static final Marker CORE = MarkerManager.getMarker("CORE"); public static final Marker LOADING = MarkerManager.getMarker("LOADING"); public static final Marker SCAN = MarkerManager.getMarker("SCAN"); + public static final Marker SPLASH = MarkerManager.getMarker("SPLASH"); + public static final Marker MODELLOADING = MarkerManager.getMarker("MODELLOADING"); } diff --git a/src/main/java/net/minecraftforge/fml/ModThreadContext.java b/src/main/java/net/minecraftforge/fml/ModThreadContext.java new file mode 100644 index 000000000..107025430 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/ModThreadContext.java @@ -0,0 +1,37 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import net.minecraftforge.fml.common.ModContainer; + +public class ModThreadContext +{ + private static ThreadLocal context = ThreadLocal.withInitial(ModThreadContext::new); + + public static ModThreadContext get() { + return context.get(); + } + + private ModContainer currentContainer; + + public ModContainer getCurrentContainer() { + return currentContainer == null ? DefaultModContainers.MINECRAFT : currentContainer; + } +} diff --git a/src/main/java/net/minecraftforge/fml/SidedExecutor.java b/src/main/java/net/minecraftforge/fml/SidedExecutor.java new file mode 100644 index 000000000..d77f59ee7 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/SidedExecutor.java @@ -0,0 +1,52 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import net.minecraftforge.api.Side; + +import java.util.concurrent.Callable; +import java.util.function.Supplier; + +public final class SidedExecutor +{ + private SidedExecutor() {} + + /** + * Run the callable in the supplier only on the specified {@link Side} + * + * @param side The side to run on + * @param toRun A supplier of the callable to run (Supplier wrapper to ensure classloading only on the appropriate side) + * @param The return type from the callable + * @return The callable's result + */ + public static T runOn(Side side, Supplier> toRun) { + if (side == Side.CLIENT) { + try + { + return toRun.get().call(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fml/StringSubstitutor.java b/src/main/java/net/minecraftforge/fml/StringSubstitutor.java new file mode 100644 index 000000000..bb6d62fe1 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/StringSubstitutor.java @@ -0,0 +1,67 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import com.google.common.collect.ImmutableMap; +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import org.apache.commons.lang3.text.StrLookup; +import org.apache.commons.lang3.text.StrSubstitutor; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class StringSubstitutor +{ + private static final Map globals = ImmutableMap.of( + "mcVersion", ForgeVersion.mcVersion, + "forgeVersion", ForgeVersion.getVersion(), + "mcpVersion", ForgeVersion.mcpVersion); + + public static String replace(final String in, final ModFile file) { + return new StrSubstitutor(getStringLookup(file)).replace(in); + } + + private static StrLookup getStringLookup(final ModFile file) { + return new StrLookup() + { + @Override + public String lookup(String key) + { + final String[] parts = key.split("\\."); + if (parts.length == 1) return key; + final String pfx = parts[0]; + if ("global".equals(pfx)) + { + return globals.get(parts[1]); + } + else if ("file".equals(pfx)) + { + return String.valueOf(file.getSubstitutionMap().get().get(parts[1])); + } + return key; + } + }; + } + private static List split(final String in) { + return Arrays.asList(in.split(".")); + } +} diff --git a/src/main/java/net/minecraftforge/fml/StringUtils.java b/src/main/java/net/minecraftforge/fml/StringUtils.java index 5d2b8f3ef..f38adc7e9 100644 --- a/src/main/java/net/minecraftforge/fml/StringUtils.java +++ b/src/main/java/net/minecraftforge/fml/StringUtils.java @@ -19,6 +19,13 @@ package net.minecraftforge.fml; +import org.apache.commons.lang3.text.StrSubstitutor; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.Optional; + /** * Created by cpw on 05/06/17. */ @@ -32,4 +39,19 @@ public class StringUtils String lowerSearch = toLowerCase(search); return java.util.stream.Stream.of(endings).anyMatch(lowerSearch::endsWith); } + + public static URL toURL(final String string) { + try + { + return new URL(string); + } + catch (MalformedURLException e) + { + throw new RuntimeException(e); + } + } + + public static String parseStringFormat(final String input, final Map properties) { + return StrSubstitutor.replace(input, properties); + } } diff --git a/src/main/java/net/minecraftforge/fml/TomlConverters.java b/src/main/java/net/minecraftforge/fml/TomlConverters.java new file mode 100644 index 000000000..99f6c5940 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/TomlConverters.java @@ -0,0 +1,50 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml; + +import com.electronwill.nightconfig.core.conversion.Converter; + +import java.net.MalformedURLException; +import java.net.URL; + +public final class TomlConverters +{ + public static class StringToURL implements Converter { + + @Override + public URL convertToField(String value) + { + try + { + return new URL(value); + } + catch (MalformedURLException e) + { + throw new RuntimeException("Invalid URL specified", e); + } + } + + @Override + public String convertFromField(URL value) + { + return value.toString(); + } + } +} diff --git a/src/main/java/net/minecraftforge/fml/client/BrandingControl.java b/src/main/java/net/minecraftforge/fml/client/BrandingControl.java new file mode 100644 index 000000000..ed8f11c06 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/client/BrandingControl.java @@ -0,0 +1,66 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.client; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import net.minecraftforge.common.ForgeVersion; + +import java.text.MessageFormat; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class BrandingControl +{ + private static List brandings; + private static List brandingsNoMC; + + private static void computeBranding() + { + if (brandings == null) + { + ImmutableList.Builder brd = ImmutableList.builder(); + brd.add("Minecraft " + ForgeVersion.mcVersion); + brd.add("MCP " + ForgeVersion.mcpVersion); + brd.add("Forge " + ForgeVersion.getVersion()); + int tModCount = 2; + + brd.add(MessageFormat.format("{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded", tModCount)); + brandings = brd.build(); + brandingsNoMC = brandings.subList(1, brandings.size()); + } + } + + public static List getBrandings(boolean includeMC, boolean reverse) + { + computeBranding(); + if (includeMC) { + return reverse ? Lists.reverse(brandings) : brandings; + } else { + return reverse ? Lists.reverse(brandingsNoMC) : brandingsNoMC; + } + } + + private static final List defaultClientBranding = Stream.of("fml", "forge").collect(Collectors.toList()); + public static String getClientBranding() { + return defaultClientBranding.stream().collect(Collectors.joining(",")); + } +} diff --git a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java new file mode 100644 index 000000000..72d423444 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java @@ -0,0 +1,65 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.IReloadableResourceManager; +import net.minecraft.client.resources.IResourcePack; +import net.minecraft.client.resources.data.MetadataSerializer; +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.loading.FMLLoader; + +import java.util.List; + +public class ClientModLoader +{ + /** + * Mod loading entrypoint for the client + * @param minecraft + * @param defaultResourcePacks + * @param mcResourceManager + * @param metadataSerializer_ + */ + public static void begin(Minecraft minecraft, List defaultResourcePacks, IReloadableResourceManager mcResourceManager, MetadataSerializer metadataSerializer_) + { + SplashProgress.start(); + FMLLoader.getModLoader().loadMods(); + minecraft.refreshResources(); + } + + public static void end() + { + SplashProgress.finish(); + } + + public static ForgeVersion.Status checkForUpdates() + { + return ForgeVersion.Status.UP_TO_DATE; + } + + public static void complete() + { + GlStateManager.disableTexture2D(); + GlStateManager.enableTexture2D(); + } +} diff --git a/src/main/java/net/minecraftforge/fml/client/FMLClientConfig.java b/src/main/java/net/minecraftforge/fml/client/FMLClientConfig.java new file mode 100644 index 000000000..8395db92f --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/client/FMLClientConfig.java @@ -0,0 +1,25 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.client; + +public class FMLClientConfig +{ + +} diff --git a/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java b/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java index 54247bcaa..99654098f 100644 --- a/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java +++ b/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java @@ -1031,13 +1031,6 @@ public class FMLClientHandler implements IFMLSidedHandler @Override public void processWindowMessages() { - // workaround for windows requiring messages being processed on the main thread - if (LWJGLUtil.getPlatform() != LWJGLUtil.PLATFORM_WINDOWS) return; - // If we can't grab the mutex, the update call is blocked, probably in native code, just skip it and carry on - // We'll get another go next time - if (!SplashProgress.mutex.tryAcquire()) return; - Display.processMessages(); - SplashProgress.mutex.release(); } // From FontRenderer.renderCharAtPos private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"; diff --git a/src/main/java/net/minecraftforge/fml/client/SplashProgress.java b/src/main/java/net/minecraftforge/fml/client/SplashProgress.java index 6890ec7be..891949606 100644 --- a/src/main/java/net/minecraftforge/fml/client/SplashProgress.java +++ b/src/main/java/net/minecraftforge/fml/client/SplashProgress.java @@ -19,6 +19,8 @@ package net.minecraftforge.fml.client; +import static net.minecraftforge.fml.Logging.SPLASH; +import static net.minecraftforge.fml.Logging.fmlLog; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL12.*; @@ -60,18 +62,18 @@ import net.minecraft.client.resources.SimpleResource; import net.minecraft.crash.CrashReport; import net.minecraft.launchwrapper.Launch; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.CrashReportExtender; import net.minecraftforge.fml.common.EnhancedRuntimeException; -import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.ICrashCallable; import net.minecraftforge.fml.common.ProgressManager; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; -import net.minecraftforge.fml.common.asm.FMLSanityChecker; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; +import org.lwjgl.LWJGLUtil; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Drawable; import org.lwjgl.opengl.SharedDrawable; @@ -94,7 +96,7 @@ public class SplashProgress private static SplashFontRenderer fontRenderer; private static final IResourcePack mcPack = Minecraft.getMinecraft().mcDefaultResourcePack; - private static final IResourcePack fmlPack = createResourcePack(FMLSanityChecker.fmlLocation); + private static final IResourcePack fmlPack = mcPack; private static IResourcePack miscPack; private static Texture fontTexture; @@ -121,7 +123,18 @@ public class SplashProgress private static final int TIMING_FRAME_COUNT = 200; private static final int TIMING_FRAME_THRESHOLD = TIMING_FRAME_COUNT * 5 * 1000000; // 5 ms per frame, scaled to nanos - static final Semaphore mutex = new Semaphore(1); + private static final Semaphore mutex = new Semaphore(1); + + public static Void processMessages() { + // workaround for windows requiring messages being processed on the main thread + if (LWJGLUtil.getPlatform() != LWJGLUtil.PLATFORM_WINDOWS) return null; + // If we can't grab the mutex, the update call is blocked, probably in native code, just skip it and carry on + // We'll get another go next time + if (!SplashProgress.mutex.tryAcquire()) return null; + Display.processMessages(); + SplashProgress.mutex.release(); + return null; + } private static String getString(String name, String def) { @@ -160,16 +173,10 @@ public class SplashProgress } catch(IOException e) { - FMLLog.log.info("Could not load splash.properties, will create a default one"); + fmlLog.info(SPLASH, "Could not load splash.properties, will create a default one"); } - //Some systems do not support this and have weird effects, so we need to detect and disable them by default. - //The user can always force enable it if they want to take the responsibility for bugs. - boolean defaultEnabled = true; - - // Enable if we have the flag, and there's either no optifine, or optifine has added a key to the blackboard ("optifine.ForgeSplashCompatible") - // Optifine authors - add this key to the blackboard if you feel your modifications are now compatible with this code. - enabled = getBool("enabled", defaultEnabled) && ( (!FMLClientHandler.instance().hasOptifine()) || Launch.blackboard.containsKey("optifine.ForgeSplashCompatible")); + enabled = getBool("enabled", true); rotate = getBool("rotate", false); showMemory = getBool("showMemory", true); logoOffset = getInt("logoOffset", 0); @@ -202,7 +209,7 @@ public class SplashProgress if(!enabled) return; // getting debug info out of the way, while we still can - FMLCommonHandler.instance().registerCrashCallable(new ICrashCallable() + CrashReportExtender.registerCrashCallable(new ICrashCallable() { @Override public String call() throws Exception @@ -222,7 +229,7 @@ public class SplashProgress CrashReport report = CrashReport.makeCrashReport(new Throwable(), "Loading screen debug info"); StringBuilder systemDetailsBuilder = new StringBuilder(); report.getCategory().appendToStringBuilder(systemDetailsBuilder); - FMLLog.log.info(systemDetailsBuilder.toString()); + fmlLog.info(SPLASH, systemDetailsBuilder.toString()); try { @@ -390,13 +397,13 @@ public class SplashProgress if (!isDisplayVSyncForced) { isDisplayVSyncForced = true; - FMLLog.log.info("Using alternative sync timing : {} frames of Display.update took {} nanos", TIMING_FRAME_COUNT, updateTiming); + fmlLog.info(SPLASH,"Using alternative sync timing : {} frames of Display.update took {} nanos", TIMING_FRAME_COUNT, updateTiming); } try { Thread.sleep(16); } catch (InterruptedException ie) {} } else { if (framecount ==TIMING_FRAME_COUNT) { - FMLLog.log.info("Using sync timing. {} frames of Display.update took {} nanos", TIMING_FRAME_COUNT, updateTiming); + fmlLog.info("Using sync timing. {} frames of Display.update took {} nanos", TIMING_FRAME_COUNT, updateTiming); } Display.sync(100); } diff --git a/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java b/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java index 0311ec8b4..a30ac203a 100644 --- a/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import net.minecraftforge.fml.StringUtils; import net.minecraftforge.fml.common.versioning.ArtifactVersion; import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; import net.minecraftforge.fml.common.versioning.VersionRange; @@ -97,6 +98,12 @@ public class DummyModContainer implements ModContainer return md.modId; } + @Override + public String getPrefix() + { + return StringUtils.toLowerCase(getModId()); + } + @Override public String getName() { diff --git a/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java b/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java index 45716bc04..30842428b 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java @@ -19,27 +19,18 @@ package net.minecraftforge.fml.common; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.ref.WeakReference; -import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; -import net.minecraft.crash.CrashReport; -import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; @@ -61,6 +52,7 @@ import net.minecraftforge.client.model.animation.Animation; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.CompoundDataFixer; +import net.minecraftforge.fml.client.BrandingControl; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.EventBus; import net.minecraftforge.fml.common.gameevent.InputEvent; @@ -70,22 +62,17 @@ import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; import net.minecraftforge.fml.common.network.FMLNetworkEvent; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.thread.SidedThreadGroup; -import net.minecraftforge.fml.relauncher.CoreModManager; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.server.FMLServerHandler; -import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.Logger; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; -import javax.annotation.Nullable; - /** * The main class for non-obfuscated hook handling code * @@ -113,7 +100,6 @@ public class FMLCommonHandler private boolean noForge; private List brandings; private List brandingsNoMC; - private List crashCallables = Lists.newArrayList(Loader.instance().getCallableCrashInformation()); private Set handlerSet = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); private WeakReference handlerToCheck; private EventBus eventBus = MinecraftForge.EVENT_BUS; @@ -121,26 +107,6 @@ public class FMLCommonHandler private FMLCommonHandler() { - registerCrashCallable(new ICrashCallable() - { - @Override - public String call() throws Exception - { - StringBuilder builder = new StringBuilder(); - Joiner joiner = Joiner.on("\n "); - for(String coreMod : CoreModManager.getTransformers().keySet()) - { - builder.append("\n" + coreMod + "\n ").append(joiner.join(CoreModManager.getTransformers().get(coreMod))); - } - return builder.toString(); - } - - @Override - public String getLabel() - { - return "Loaded coremods (and transformers)"; - } - }); } /** * The FML event bus. Subscribe here for FML related events @@ -224,34 +190,11 @@ public class FMLCommonHandler } - public void computeBranding() - { - if (brandings == null) - { - Builder brd = ImmutableList.builder(); - brd.add(Loader.instance().getMCVersionString()); - brd.add(Loader.instance().getMCPVersionString()); - brd.add("Powered by Forge " + ForgeVersion.getVersion()); - if (sidedDelegate!=null) - { - brd.addAll(sidedDelegate.getAdditionalBrandingInformation()); - } - if (Loader.instance().getFMLBrandingProperties().containsKey("fmlbranding")) - { - brd.add(Loader.instance().getFMLBrandingProperties().get("fmlbranding")); - } - int tModCount = Loader.instance().getModList().size(); - int aModCount = Loader.instance().getActiveModList().size(); - brd.add(String.format("%d mod%s loaded, %d mod%s active", tModCount, tModCount!=1 ? "s" :"", aModCount, aModCount!=1 ? "s" :"" )); - brandings = brd.build(); - brandingsNoMC = brandings.subList(1, brandings.size()); - } - } public List getBrandings(boolean includeMC) { if (brandings == null) { - computeBranding(); + BrandingControl.computeBranding(); } return includeMC ? ImmutableList.copyOf(brandings) : ImmutableList.copyOf(brandingsNoMC); } @@ -370,19 +313,6 @@ public class FMLCommonHandler bus().post(new TickEvent.PlayerTickEvent(Phase.END, player)); } - public void registerCrashCallable(ICrashCallable callable) - { - crashCallables.add(callable); - } - - public void enhanceCrashReport(CrashReport crashReport, CrashReportCategory category) - { - for (ICrashCallable call: crashCallables) - { - category.addDetail(call.getLabel(), call); - } - } - public void handleWorldDataSave(SaveHandler handler, WorldInfo worldInfo, NBTTagCompound tagCompound) { for (ModContainer mc : Loader.instance().getModList()) @@ -709,47 +639,6 @@ public class FMLCommonHandler } } - /** - * Loads a lang file, first searching for a marker to enable the 'extended' format {escape characters} - * If the marker is not found it simply returns and let the vanilla code load things. - * The Marker is 'PARSE_ESCAPES' by itself on a line starting with '#' as such: - * #PARSE_ESCAPES - * - * @param table The Map to load each key/value pair into. - * @param inputstream Input stream containing the lang file. - * @return A new InputStream that vanilla uses to load normal Lang files, Null if this is a 'enhanced' file and loading is done. - */ - @Nullable - public InputStream loadLanguage(Map table, InputStream inputstream) throws IOException - { - byte[] data = IOUtils.toByteArray(inputstream); - - boolean isEnhanced = false; - for (String line : IOUtils.readLines(new ByteArrayInputStream(data), StandardCharsets.UTF_8)) - { - if (!line.isEmpty() && line.charAt(0) == '#') - { - line = line.substring(1).trim(); - if (line.equals("PARSE_ESCAPES")) - { - isEnhanced = true; - break; - } - } - } - - if (!isEnhanced) - return new ByteArrayInputStream(data); - - Properties props = new Properties(); - props.load(new InputStreamReader(new ByteArrayInputStream(data), StandardCharsets.UTF_8)); - for (Entry e : props.entrySet()) - { - table.put((String)e.getKey(), (String)e.getValue()); - } - props.clear(); - return null; - } public String stripSpecialChars(String message) { return sidedDelegate != null ? sidedDelegate.stripSpecialChars(message) : message; diff --git a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java index 7278ea677..870015ded 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java @@ -54,10 +54,13 @@ import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; import net.minecraftforge.fml.common.versioning.DependencyParser; import net.minecraftforge.fml.common.versioning.VersionParser; import net.minecraftforge.fml.common.versioning.VersionRange; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.fml.relauncher.Side; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -73,10 +76,11 @@ import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import org.apache.logging.log4j.message.FormattedMessage; import javax.annotation.Nullable; +import static net.minecraftforge.fml.Logging.CORE; + public class FMLModContainer implements ModContainer { private Object modInstance; @@ -105,6 +109,8 @@ public class FMLModContainer implements ModContainer private URL updateJSONUrl; private int classVersion; + private Logger modLog; + public FMLModContainer(String className, ModCandidate container, Map modDescriptor) { this.className = className; @@ -126,6 +132,22 @@ public class FMLModContainer implements ModContainer FMLLog.log.trace("Using custom language adapter {} for {} (modid: {})", languageAdapterType, this.className, getModId()); } sanityCheckModId(); + + modLog = LogManager.getLogger(getModId()); + } + + public FMLModContainer(ModInfo info, String className, ClassLoader modClassLoader) + { + try + { + final Class aClass = Class.forName(className, true, modClassLoader); + LogManager.getLogger("FML").error("Loaded {} with {}", aClass, aClass.getClassLoader()); + } + catch (ClassNotFoundException e) + { + LogManager.getLogger("FML").error(CORE, "Failed to load class {}", className, e); + throw new RuntimeException(e); + } } private void sanityCheckModId() @@ -212,15 +234,15 @@ public class FMLModContainer implements ModContainer modMetadata.requiredMods = info.requirements; modMetadata.dependencies = info.dependencies; modMetadata.dependants = info.dependants; - FMLLog.log.trace("Parsed dependency info for {}: Requirements: {} After:{} Before:{}", getModId(), info.requirements, info.dependencies, info.dependants); + modLog.trace("Parsed dependency info : Requirements: {} After:{} Before:{}", info.requirements, info.dependencies, info.dependants); } else { - FMLLog.log.trace("Using mcmod dependency info for {}: {} {} {}", getModId(), modMetadata.requiredMods, modMetadata.dependencies, modMetadata.dependants); + modLog.trace("Using mcmod dependency info : {} {} {}", modMetadata.requiredMods, modMetadata.dependencies, modMetadata.dependants); } if (Strings.isNullOrEmpty(modMetadata.name)) { - FMLLog.log.info("Mod {} is missing the required element 'name'. Substituting {}", getModId(), getModId()); + modLog.info("Mod {} is missing the required element 'name'. Substituting {}", getModId(), getModId()); modMetadata.name = getModId(); } internalVersion = (String)descriptor.get("version"); @@ -230,18 +252,18 @@ public class FMLModContainer implements ModContainer if (versionProps != null) { internalVersion = versionProps.getProperty(getModId() + ".version"); - FMLLog.log.debug("Found version {} for mod {} in version.properties, using", internalVersion, getModId()); + modLog.debug("Found version {} for mod {} in version.properties, using", internalVersion, getModId()); } } if (Strings.isNullOrEmpty(internalVersion) && !Strings.isNullOrEmpty(modMetadata.version)) { - FMLLog.log.warn("Mod {} is missing the required element 'version' and a version.properties file could not be found. Falling back to metadata version {}", getModId(), modMetadata.version); + modLog.warn("Mod {} is missing the required element 'version' and a version.properties file could not be found. Falling back to metadata version {}", getModId(), modMetadata.version); internalVersion = modMetadata.version; } if (Strings.isNullOrEmpty(internalVersion)) { - FMLLog.log.warn("Mod {} is missing the required element 'version' and no fallback can be found. Substituting '1.0'.", getModId()); + modLog.warn("Mod {} is missing the required element 'version' and no fallback can be found. Substituting '1.0'.", getModId()); modMetadata.version = internalVersion = "1.0"; } @@ -269,7 +291,7 @@ public class FMLModContainer implements ModContainer } catch (MalformedURLException e) { - FMLLog.log.debug("Specified json URL for mod '{}' is invalid: {}", getModId(), jsonURL); + modLog.debug("Specified json URL invalid: {}", jsonURL); } } } @@ -279,7 +301,7 @@ public class FMLModContainer implements ModContainer { try { - FMLLog.log.debug("Attempting to load the file version.properties from {} to locate a version number for mod {}", getSource().getName(), getModId()); + modLog.debug("Attempting to load the file version.properties from {} to locate a version number for {}", getSource().getName(), getModId()); Properties version = null; if (getSource().isFile()) { @@ -316,7 +338,7 @@ public class FMLModContainer implements ModContainer } catch (IOException e) { - FMLLog.log.trace("Failed to find a usable version.properties file for mod {}", getModId()); + modLog.trace("Failed to find a usable version.properties file"); return null; } } @@ -368,7 +390,7 @@ public class FMLModContainer implements ModContainer { if (this.enabled) { - FMLLog.log.debug("Enabling mod {}", getModId()); + modLog.debug("Enabling mod {}", getModId()); this.eventBus = bus; this.controller = controller; eventBus.register(this); @@ -381,7 +403,8 @@ public class FMLModContainer implements ModContainer } @Nullable - private Method gatherAnnotations(Class clazz) + @SuppressWarnings("unchecked") + private Method gatherAnnotations(Class clazz) throws Exception { Method factoryMethod = null; for (Method m : clazz.getDeclaredMethods()) @@ -393,13 +416,11 @@ public class FMLModContainer implements ModContainer if (m.getParameterTypes().length == 1 && FMLEvent.class.isAssignableFrom(m.getParameterTypes()[0])) { m.setAccessible(true); - @SuppressWarnings("unchecked") - Class parameterType = (Class) m.getParameterTypes()[0]; - eventMethods.put(parameterType, m); + eventMethods.put((Class)m.getParameterTypes()[0], m); } else { - FMLLog.log.error("The mod {} appears to have an invalid event annotation {}. This annotation can only apply to methods with recognized event arguments - it will not be called", getModId(), a.annotationType().getSimpleName()); + modLog.error("The mod {} appears to have an invalid event annotation {}. This annotation can only apply to methods with recognized event arguments - it will not be called", getModId(), a.annotationType().getSimpleName()); } } else if (a.annotationType().equals(Mod.InstanceFactory.class)) @@ -411,11 +432,11 @@ public class FMLModContainer implements ModContainer } else if (!(Modifier.isStatic(m.getModifiers()) && m.getParameterTypes().length == 0)) { - FMLLog.log.error("The InstanceFactory annotation can only apply to a static method, taking zero arguments - it will be ignored on {}({}) for mod {}", m.getName(), Arrays.asList(m.getParameterTypes()), getModId()); + modLog.error("The InstanceFactory annotation can only apply to a static method, taking zero arguments - it will be ignored on {}({})", m.getName(), Arrays.asList(m.getParameterTypes())); } else if (factoryMethod != null) { - FMLLog.log.error("The InstanceFactory annotation can only be used once, the application to {}({}) will be ignored for mod {}", m.getName(), Arrays.asList(m.getParameterTypes()), getModId()); + modLog.error("The InstanceFactory annotation can only be used once, the application to {}({}) will be ignored", m.getName(), Arrays.asList(m.getParameterTypes())); } } } @@ -423,7 +444,7 @@ public class FMLModContainer implements ModContainer return factoryMethod; } - private void processFieldAnnotations(ASMDataTable asmDataTable) throws IllegalAccessException + private void processFieldAnnotations(ASMDataTable asmDataTable) throws Exception { SetMultimap annotations = asmDataTable.getAnnotationsFor(this); @@ -482,7 +503,7 @@ public class FMLModContainer implements ModContainer } catch (ReflectiveOperationException e) { - FMLLog.log.warn("Attempting to load @{} in class {} for {} and failing", annotationName, targets.getClassName(), mc.getModId(), e); + modLog.warn("Attempting to load @{} in class {} for {} and failing", annotationName, targets.getClassName(), mc.getModId(), e); } } if (f != null) @@ -493,7 +514,7 @@ public class FMLModContainer implements ModContainer target = modInstance; if (!modInstance.getClass().equals(clz)) { - FMLLog.log.warn("Unable to inject @{} in non-static field {}.{} for {} as it is NOT the primary mod instance", annotationName, targets.getClassName(), targets.getObjectName(), mc.getModId()); + modLog.warn("Unable to inject @{} in non-static field {}.{} for {} as it is NOT the primary mod instance", annotationName, targets.getClassName(), targets.getObjectName(), mc.getModId()); continue; } } @@ -505,108 +526,85 @@ public class FMLModContainer implements ModContainer @Subscribe public void constructMod(FMLConstructionEvent event) { - ModClassLoader modClassLoader = event.getModClassLoader(); try { + ModClassLoader modClassLoader = event.getModClassLoader(); modClassLoader.addFile(source); - } - catch (MalformedURLException e) - { - FormattedMessage message = new FormattedMessage("{} Failed to add file to classloader: {}", getModId(), source); - throw new LoaderException(message.getFormattedMessage(), e); - } - modClassLoader.clearNegativeCacheFor(candidate.getClassList()); + modClassLoader.clearNegativeCacheFor(candidate.getClassList()); - //Only place I could think to add this... - MinecraftForge.preloadCrashClasses(event.getASMHarvestedData(), getModId(), candidate.getClassList()); + //Only place I could think to add this... + MinecraftForge.preloadCrashClasses(event.getASMHarvestedData(), getModId(), candidate.getClassList()); - Class clazz; - try - { - clazz = Class.forName(className, true, modClassLoader); - } - catch (ClassNotFoundException e) - { - FormattedMessage message = new FormattedMessage("{} Failed load class: {}", getModId(), className); - throw new LoaderException(message.getFormattedMessage(), e); - } + Class clazz = Class.forName(className, true, modClassLoader); - Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates(); - ImmutableList certList = CertificateHelper.getFingerprints(certificates); - sourceFingerprints = ImmutableSet.copyOf(certList); + Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates(); + ImmutableList certList = CertificateHelper.getFingerprints(certificates); + sourceFingerprints = ImmutableSet.copyOf(certList); - String expectedFingerprint = (String)descriptor.get("certificateFingerprint"); + String expectedFingerprint = (String)descriptor.get("certificateFingerprint"); - fingerprintNotPresent = true; + fingerprintNotPresent = true; - if (expectedFingerprint != null && !expectedFingerprint.isEmpty()) - { - if (!sourceFingerprints.contains(expectedFingerprint)) + if (expectedFingerprint != null && !expectedFingerprint.isEmpty()) { - Level warnLevel = source.isDirectory() ? Level.TRACE : Level.ERROR; - FMLLog.log.log(warnLevel, "The mod {} is expecting signature {} for source {}, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName()); + if (!sourceFingerprints.contains(expectedFingerprint)) + { + Level warnLevel = Level.ERROR; + if (source.isDirectory()) + { + warnLevel = Level.TRACE; + } + modLog.log(warnLevel, "The mod {} is expecting signature {} for source {}, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName()); + } + else + { + certificate = certificates[certList.indexOf(expectedFingerprint)]; + fingerprintNotPresent = false; + } + } + + @SuppressWarnings("unchecked") + List> props = (List>)descriptor.get("customProperties"); + if (props != null) + { + com.google.common.collect.ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Map p : props) + { + builder.put((String)p.get("k"), (String)p.get("v")); + } + customModProperties = builder.build(); } else { - certificate = certificates[certList.indexOf(expectedFingerprint)]; - fingerprintNotPresent = false; + customModProperties = EMPTY_PROPERTIES; } - } - @SuppressWarnings("unchecked") - List> props = (List>)descriptor.get("customProperties"); - if (props != null) - { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map p : props) + Boolean hasDisableableFlag = (Boolean)descriptor.get("canBeDeactivated"); + boolean hasReverseDepends = !event.getReverseDependencies().get(getModId()).isEmpty(); + if (hasDisableableFlag != null && hasDisableableFlag) { - builder.put(p.get("k"), p.get("v")); + disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.YES; } - customModProperties = builder.build(); - } - else - { - customModProperties = EMPTY_PROPERTIES; - } + 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()); + if (fingerprintNotPresent) + { + eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint)); + } + ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide(), getLanguageAdapter()); + AutomaticEventSubscriber.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); + ConfigManager.sync(this.getModId(), Config.Type.INSTANCE); - 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); - ILanguageAdapter languageAdapter = getLanguageAdapter(); - try - { - modInstance = languageAdapter.getNewInstance(this, clazz, modClassLoader, factoryMethod); - } - catch (Exception e) - { - FormattedMessage message = new FormattedMessage("{} Failed to load new mod instance.", getModId()); - throw new LoaderException(message.getFormattedMessage(), e); - } - NetworkRegistry.INSTANCE.register(this, clazz, (String)(descriptor.getOrDefault("acceptableRemoteVersions", null)), event.getASMHarvestedData()); - if (fingerprintNotPresent) - { - eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint)); - } - ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide(), languageAdapter); - AutomaticEventSubscriber.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); - ConfigManager.sync(this.getModId(), Config.Type.INSTANCE); - - try - { processFieldAnnotations(event.getASMHarvestedData()); } - catch (IllegalAccessException e) + catch (Throwable e) { - FormattedMessage message = new FormattedMessage("{} Failed to process field annotations.", getModId()); - throw new LoaderException(message.getFormattedMessage(), e); + controller.errorOccurred(this, e); } } diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index 157005896..f94affa43 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -757,19 +757,6 @@ public class Loader public ICrashCallable getCallableCrashInformation() { - return new ICrashCallable() { - @Override - public String call() throws Exception - { - return getCrashInformation(); - } - - @Override - public String getLabel() - { - return "FML"; - } - }; } public List getActiveModList() diff --git a/src/main/java/net/minecraftforge/fml/common/Mod.java b/src/main/java/net/minecraftforge/fml/common/Mod.java index 781f72b52..73074e558 100644 --- a/src/main/java/net/minecraftforge/fml/common/Mod.java +++ b/src/main/java/net/minecraftforge/fml/common/Mod.java @@ -61,42 +61,6 @@ public @interface Mod * By default, you will have a resource domain that matches the modid. All these uses require that constraints are imposed on the format of the modid. */ String modid(); - - /** - * An optional GUI factory for this mod. This is the name of a class implementing {@link IModGuiFactory} that will be instantiated - * on the client side, and will have certain configuration/options guis requested from it. - * - * @return The name of a class implementing {@link IModGuiFactory} - */ - String guiFactory() default ""; - - /** - * A list of custom properties for this mod. Completely up to the mod author if/when they - * want to put anything in here. - * @return an optional list of custom properties - */ - CustomProperty[] customProperties() default {}; - - /** - * A custom key => value property pair for use with {@link Mod#customProperties()} - * @author cpw - * - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({}) - @interface CustomProperty - { - /** - * A key. Should be unique. - * @return A key - */ - String k(); - /** - * A value. Can be anything. - * @return A value - */ - String v(); - } /** * Marks the associated method as handling an FML lifecycle event. * The method must have a single parameter, one of the following types. This annotation diff --git a/src/main/java/net/minecraftforge/fml/common/ModContainer.java b/src/main/java/net/minecraftforge/fml/common/ModContainer.java index 004d2fb5a..1442fc0a6 100644 --- a/src/main/java/net/minecraftforge/fml/common/ModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/ModContainer.java @@ -51,11 +51,13 @@ public interface ModContainer { public static enum Disableable { YES, RESTART, NEVER, DEPENDENCIES; + } /** * The globally unique modid for this mod */ String getModId(); + String getPrefix(); /** * A human readable name diff --git a/src/main/java/net/minecraftforge/fml/common/ProgressManager.java b/src/main/java/net/minecraftforge/fml/common/ProgressManager.java index a82d5dbe2..c3b56a00c 100644 --- a/src/main/java/net/minecraftforge/fml/common/ProgressManager.java +++ b/src/main/java/net/minecraftforge/fml/common/ProgressManager.java @@ -24,6 +24,16 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import com.google.common.base.Joiner; +import net.minecraftforge.api.Side; +import net.minecraftforge.fml.SidedExecutor; +import net.minecraftforge.fml.client.SplashProgress; +import org.apache.logging.log4j.message.MessageFormatMessage; +import org.apache.logging.log4j.message.StringFormattedMessage; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.opengl.Display; + +import static net.minecraftforge.fml.Logging.SPLASH; +import static net.minecraftforge.fml.Logging.fmlLog; /** * Not a fully fleshed out API, may change in future MC versions. @@ -52,7 +62,7 @@ public class ProgressManager { bar.timeEachStep(); } - FMLCommonHandler.instance().processWindowMessages(); + SidedExecutor.runOn(Side.CLIENT, ()->SplashProgress::processMessages); return bar; } @@ -71,17 +81,13 @@ public class ProgressManager { long newTime = System.nanoTime(); if (bar.timeEachStep) - { - String timeString = String.format("%.3f", ((float) (newTime - bar.lastTime) / 1000000 / 1000)); - FMLLog.log.debug("Bar Step: {} - {} took {}s", bar.getTitle(), bar.getMessage(), timeString); - } - String timeString = String.format("%.3f", ((float) (newTime - bar.startTime) / 1000000 / 1000)); + fmlLog.debug(SPLASH, () -> new MessageFormatMessage("Bar Step: {0} - {1} took {2,number,0.000}ms", bar.getTitle(), bar.getMessage(), (newTime - bar.lastTime) / 1.0e6)); if (bar.getSteps() == 1) - FMLLog.log.debug("Bar Finished: {} - {} took {}s", bar.getTitle(), bar.getMessage(), timeString); + fmlLog.debug(SPLASH, () -> new MessageFormatMessage("Bar Finished: {0} - {1} took {2,number,0.000}ms", bar.getTitle(), bar.getMessage(), (newTime - bar.lastTime) / 1.0e6)); else - FMLLog.log.debug("Bar Finished: {} took {}s", bar.getTitle(), timeString); + fmlLog.debug(SPLASH, () -> new MessageFormatMessage("Bar Finished: {0} took {1,number,0.000}ms", bar.getTitle(), (newTime - bar.lastTime) / 1.0e6)); } - FMLCommonHandler.instance().processWindowMessages(); + SidedExecutor.runOn(Side.CLIENT, ()->SplashProgress::processMessages); } /* @@ -124,7 +130,7 @@ public class ProgressManager if (timeEachStep && step != 0) { long newTime = System.nanoTime(); - FMLLog.log.debug(String.format("Bar Step: %s - %s took %.3fs", getTitle(), getMessage(), ((float)(newTime - lastTime) / 1000000 / 1000))); + fmlLog.debug(SPLASH,new MessageFormatMessage("Bar Step: {0} - {1} took {2,number,0.000}ms", getTitle(), getMessage(), (newTime - lastTime) / 1.0e6)); lastTime = newTime; } step++; diff --git a/src/main/java/net/minecraftforge/fml/common/toposort/ModSorter.java b/src/main/java/net/minecraftforge/fml/common/toposort/ModSorter.java index 51841642c..ae7f64b84 100644 --- a/src/main/java/net/minecraftforge/fml/common/toposort/ModSorter.java +++ b/src/main/java/net/minecraftforge/fml/common/toposort/ModSorter.java @@ -24,8 +24,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.common.collect.Sets; import net.minecraftforge.fml.common.DummyModContainer; +import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.ModAPIManager; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.toposort.TopologicalSort.DirectedGraph; @@ -137,7 +140,23 @@ public class ModSorter public List sort() { - List sortedList = TopologicalSort.topologicalSort(modGraph); + List sortedList; + try + { + sortedList = TopologicalSort.topologicalSort(modGraph); + } + catch (TopologicalSort.TopoSortException tse) + { + FMLLog.log.fatal("Mod Sorting failed."); + FMLLog.log.fatal("Visiting node {}", tse.getNode()); + FMLLog.log.fatal("Current sorted list : {}", tse.getSortedResult()); + FMLLog.log.fatal("Visited set for this node : {}", tse.getVisitedNodes()); + FMLLog.log.fatal("Explored node set : {}", tse.getExpandedNodes()); + Sets.SetView cycleList = Sets.difference(tse.getVisitedNodes(), tse.getExpandedNodes()); + FMLLog.log.fatal("Likely cycle is in : {}", cycleList); + + throw new ModSortingException("There was a cycle detected in the input graph, sorting is not possible", tse.getNode(), cycleList); + } sortedList.removeAll(Arrays.asList(new ModContainer[] {beforeAll, before, after, afterAll})); return sortedList; } diff --git a/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java b/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java index 92b7e6426..d7b3cdda1 100644 --- a/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java +++ b/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java @@ -59,7 +59,7 @@ public class ModSortingException extends EnhancedRuntimeException implements IDi public ModSortingException(String string, T node, Set visitedNodes) { super(string); - this.sortingExceptionData = new SortingExceptionData(node, visitedNodes); + this.sortingExceptionData = new SortingExceptionData<>(node, visitedNodes); } @SuppressWarnings("unchecked") diff --git a/src/main/java/net/minecraftforge/fml/common/toposort/TopologicalSort.java b/src/main/java/net/minecraftforge/fml/common/toposort/TopologicalSort.java index d9201253c..c60cf1841 100644 --- a/src/main/java/net/minecraftforge/fml/common/toposort/TopologicalSort.java +++ b/src/main/java/net/minecraftforge/fml/common/toposort/TopologicalSort.java @@ -32,11 +32,6 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import net.minecraftforge.fml.common.FMLLog; - -import com.google.common.collect.Sets; -import com.google.common.collect.Sets.SetView; - /** * Topological sort for mod loading * @@ -48,8 +43,8 @@ public class TopologicalSort { public static class DirectedGraph implements Iterable { - private final Map> graph = new HashMap>(); - private List orderedNodes = new ArrayList(); + private final Map> graph = new HashMap<>(); + private List orderedNodes = new ArrayList<>(); public boolean addNode(T node) { @@ -60,7 +55,7 @@ public class TopologicalSort } orderedNodes.add(node); - graph.put(node, new TreeSet(Comparator.comparingInt(o -> orderedNodes.indexOf(o)))); + graph.put(node, new TreeSet<>(Comparator.comparingInt(o -> orderedNodes.indexOf(o)))); return true; } @@ -136,10 +131,10 @@ public class TopologicalSort public static List topologicalSort(DirectedGraph graph) { DirectedGraph rGraph = reverse(graph); - List sortedResult = new ArrayList(); - Set visitedNodes = new HashSet(); + List sortedResult = new ArrayList<>(); + Set visitedNodes = new HashSet<>(); // A list of "fully explored" nodes. Leftovers in here indicate cycles in the graph - Set expandedNodes = new HashSet(); + Set expandedNodes = new HashSet<>(); for (T node : rGraph) { @@ -151,7 +146,7 @@ public class TopologicalSort public static DirectedGraph reverse(DirectedGraph graph) { - DirectedGraph result = new DirectedGraph(); + DirectedGraph result = new DirectedGraph<>(); for (T node : graph) { @@ -169,7 +164,7 @@ public class TopologicalSort return result; } - public static void explore(T node, DirectedGraph graph, List sortedResult, Set visitedNodes, Set expandedNodes) + private static void explore(T node, DirectedGraph graph, List sortedResult, Set visitedNodes, Set expandedNodes) { // Have we been here before? if (visitedNodes.contains(node)) @@ -181,14 +176,7 @@ public class TopologicalSort return; } - FMLLog.log.fatal("Mod Sorting failed."); - FMLLog.log.fatal("Visiting node {}", node); - FMLLog.log.fatal("Current sorted list : {}", sortedResult); - FMLLog.log.fatal("Visited set for this node : {}", visitedNodes); - FMLLog.log.fatal("Explored node set : {}", expandedNodes); - SetView cycleList = Sets.difference(visitedNodes, expandedNodes); - FMLLog.log.fatal("Likely cycle is in : {}", cycleList); - throw new ModSortingException("There was a cycle detected in the input graph, sorting is not possible", node, cycleList); + throw new TopoSortException(node, sortedResult, visitedNodes, expandedNodes); } // Visit this node @@ -205,4 +193,39 @@ public class TopologicalSort // And mark ourselves as explored expandedNodes.add(node); } + + static class TopoSortException extends RuntimeException { + private final Object node; + private final List sortedResult; + private final Set visitedNodes; + private final Set expandedNodes; + + public TopoSortException(Object node, List sortedResult, Set visitedNodes, Set expandedNodes) + { + this.node = node; + this.sortedResult = sortedResult; + this.visitedNodes = visitedNodes; + this.expandedNodes = expandedNodes; + } + + public Object getNode() + { + return node; + } + + public List getSortedResult() + { + return sortedResult; + } + + public Set getVisitedNodes() + { + return visitedNodes; + } + + public Set getExpandedNodes() + { + return expandedNodes; + } + } } diff --git a/src/main/java/net/minecraftforge/fml/common/versioning/InvalidVersionSpecificationException.java b/src/main/java/net/minecraftforge/fml/common/versioning/InvalidVersionSpecificationException.java index 7f24bcdd3..9a27c28d1 100644 --- a/src/main/java/net/minecraftforge/fml/common/versioning/InvalidVersionSpecificationException.java +++ b/src/main/java/net/minecraftforge/fml/common/versioning/InvalidVersionSpecificationException.java @@ -27,7 +27,7 @@ package net.minecraftforge.fml.common.versioning; * * @author Brett Porter */ -public class InvalidVersionSpecificationException extends Exception +public class InvalidVersionSpecificationException extends RuntimeException { private static final long serialVersionUID = 1L; diff --git a/src/main/java/net/minecraftforge/fml/hooks/LanguageHook.java b/src/main/java/net/minecraftforge/fml/hooks/LanguageHook.java new file mode 100644 index 000000000..ee802d717 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/hooks/LanguageHook.java @@ -0,0 +1,76 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.hooks; + +import org.apache.commons.io.IOUtils; + +import javax.annotation.Nullable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Properties; + +public class LanguageHook +{ + /** + * Loads a lang file, first searching for a marker to enable the 'extended' format {escape characters} + * If the marker is not found it simply returns and let the vanilla code load things. + * The Marker is 'PARSE_ESCAPES' by itself on a line starting with '#' as such: + * #PARSE_ESCAPES + * + * @param table The Map to load each key/value pair into. + * @param inputstream Input stream containing the lang file. + * @return A new InputStream that vanilla uses to load normal Lang files, Null if this is a 'enhanced' file and loading is done. + */ + @Nullable + public static InputStream loadLanguage(Map table, InputStream inputstream) throws IOException + { + byte[] data = IOUtils.toByteArray(inputstream); + + boolean isEnhanced = false; + for (String line : IOUtils.readLines(new ByteArrayInputStream(data), StandardCharsets.UTF_8)) + { + if (!line.isEmpty() && line.charAt(0) == '#') + { + line = line.substring(1).trim(); + if (line.equals("PARSE_ESCAPES")) + { + isEnhanced = true; + break; + } + } + } + + if (!isEnhanced) + return new ByteArrayInputStream(data); + + Properties props = new Properties(); + props.load(new InputStreamReader(new ByteArrayInputStream(data), StandardCharsets.UTF_8)); + for (Map.Entry e : props.entrySet()) + { + table.put((String)e.getKey(), (String)e.getValue()); + } + props.clear(); + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLCommonLaunchHandler.java b/src/main/java/net/minecraftforge/fml/loading/FMLCommonLaunchHandler.java index 0ae4095b1..a68350f31 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLCommonLaunchHandler.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLCommonLaunchHandler.java @@ -20,6 +20,8 @@ package net.minecraftforge.fml.loading; import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformingClassLoader; +import net.minecraftforge.api.Side; public abstract class FMLCommonLaunchHandler { @@ -27,4 +29,11 @@ public abstract class FMLCommonLaunchHandler { // We need to check for deobf and patched jar here and if not, build one. } + + public abstract Side getSidedness(); + + protected void beforeStart(ITransformingClassLoader launchClassLoader) + { + FMLLoader.beforeStart(launchClassLoader); + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLDevLaunchProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java similarity index 68% rename from src/main/java/net/minecraftforge/fml/loading/FMLDevLaunchProvider.java rename to src/main/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java index 8c69217cd..3c2617028 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLDevLaunchProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java @@ -21,7 +21,9 @@ package net.minecraftforge.fml.loading; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.ILaunchHandlerService; +import cpw.mods.modlauncher.api.ITransformingClassLoader; import net.minecraft.client.main.Main; +import net.minecraftforge.api.Side; import java.net.URISyntaxException; import java.nio.file.Path; @@ -31,12 +33,12 @@ import java.util.concurrent.Callable; import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.Logging.fmlLog; -public class FMLDevLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService +public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService { @Override public String name() { - return "devfml"; + return "devfmlclient"; } @Override @@ -55,10 +57,13 @@ public class FMLDevLaunchProvider extends FMLCommonLaunchHandler implements ILau } @Override - public Callable launchService(String[] arguments, ClassLoader launchClassLoader) + public Callable launchService(String[] arguments, ITransformingClassLoader launchClassLoader) { return () -> { - Main.main(arguments); + fmlLog.debug(CORE, "Launching minecraft in {} with arguments {}", launchClassLoader, arguments); + super.beforeStart(launchClassLoader); + launchClassLoader.setTargetPackageFilter(cn -> !cn.startsWith("net.minecraftforge.fml.loading.")); + Class.forName("net.minecraft.client.main.Main", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments); return null; }; } @@ -68,4 +73,10 @@ public class FMLDevLaunchProvider extends FMLCommonLaunchHandler implements ILau { fmlLog.debug(CORE, "No jar creation necessary. Launch is dev environment"); } + + @Override + public Side getSidedness() + { + return Side.CLIENT; + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java index fe23461e6..b8ef96ceb 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java @@ -19,6 +19,7 @@ package net.minecraftforge.fml.loading; +import net.minecraftforge.fml.common.FMLModContainer; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; @@ -52,9 +53,9 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider } @Override - public ModContainer loadMod(ModFile file, ClassLoader modClassLoader) + public ModContainer loadMod(final ModInfo info, final ClassLoader modClassLoader) { - return null; + return new FMLModContainer(info, className, modClassLoader); } } @@ -74,13 +75,7 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider .peek(ad -> fmlLog.debug(SCAN, "Found @Mod class {} with id {}", ad.getClassType().getClassName(), ad.getAnnotationData().get("modid"))) .map(ad -> new FMLModTarget(ad.getClassType().getClassName(), (String)ad.getAnnotationData().get("modid"))) .collect(Collectors.toMap(FMLModTarget::getModId, Function.identity())); - modTargetMap.forEach((key, value) -> scanResult.getFile().claimLanguage(key, value)); + scanResult.addLanguageLoader(modTargetMap); }; } - - @Override - public List buildModContainers(List modFiles, ClassLoader modClassLoader) - { - return null; - } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLLaunchProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLLaunchProvider.java index 5dc0d45c4..190c45d72 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLLaunchProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLLaunchProvider.java @@ -20,7 +20,9 @@ package net.minecraftforge.fml.loading; import cpw.mods.modlauncher.api.ILaunchHandlerService; +import cpw.mods.modlauncher.api.ITransformingClassLoader; import net.minecraft.client.main.Main; +import net.minecraftforge.api.Side; import java.nio.file.Path; import java.util.concurrent.Callable; @@ -40,10 +42,16 @@ public class FMLLaunchProvider extends FMLCommonLaunchHandler implements ILaunch } @Override - public Callable launchService(String[] arguments, ClassLoader launchClassLoader) + public Callable launchService(String[] arguments, ITransformingClassLoader launchClassLoader) { return () -> { return null; }; } + + @Override + public Side getSidedness() + { + return Side.CLIENT; + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java b/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java index 879087888..98e3e1b12 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java @@ -22,10 +22,14 @@ package net.minecraftforge.fml.loading; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.ILaunchHandlerService; import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformingClassLoader; import cpw.mods.modlauncher.api.IncompatibleEnvironmentException; import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; +import net.minecraftforge.api.Side; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.fml.common.FMLPaths; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler; import net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.forgespi.ICoreModProvider; @@ -51,6 +55,10 @@ public class FMLLoader private static ModDiscoverer modDiscoverer; private static ICoreModProvider coreModProvider; private static LanguageLoadingProvider languageLoadingProvider; + private static Side side; + private static ModList modList; + private static ClassLoader launchClassLoader; + private static ModLoader modLoader; static void onInitialLoad(IEnvironment environment, Set otherServices) throws IncompatibleEnvironmentException { @@ -90,29 +98,31 @@ public class FMLLoader languageLoadingProvider = new LanguageLoadingProvider(); } - static void setupLaunchHandler(final IEnvironment environment) throws IncompatibleEnvironmentException + static void setupLaunchHandler(final IEnvironment environment) { final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING"); final Optional launchHandler = environment.findLaunchHandler(launchTarget); fmlLog.debug(CORE, "Using {} as launch service", launchTarget); if (!launchHandler.isPresent()) { fmlLog.error(CORE,"Missing LaunchHandler {}, cannot continue", launchTarget); - throw new IncompatibleEnvironmentException("Missing launch handler"); + throw new RuntimeException("Missing launch handler"); } if (!(launchHandler.get() instanceof FMLCommonLaunchHandler)) { fmlLog.error(CORE, "Incompatible Launch handler found - type {}, cannot continue", launchHandler.get().getClass().getName()); - throw new IncompatibleEnvironmentException("Incompatible launch handler found"); + throw new RuntimeException("Incompatible launch handler found"); } FMLCommonLaunchHandler commonLaunchHandler = (FMLCommonLaunchHandler)launchHandler.get(); commonLaunchHandler.setup(environment); + side = commonLaunchHandler.getSidedness(); } public static void beginModScan() { fmlLog.debug(SCAN,"Scanning for Mod Locators"); modDiscoverer = new ModDiscoverer(); - modDiscoverer.discoverMods(); + final BackgroundScanHandler backgroundScanHandler = modDiscoverer.discoverMods(); + modList = backgroundScanHandler.getModList(); } public static ICoreModProvider getCoreModProvider() { @@ -147,4 +157,20 @@ public class FMLLoader fmlLog.debug(SCAN, "Adding Access Transformer in {}", modName.getFilePath()); accessTransformer.addResource(atPath, modName.getFileName()); } + + public static Side getSide() + { + return side; + } + + public static void beforeStart(ITransformingClassLoader launchClassLoader) + { + modLoader = new ModLoader(launchClassLoader.getInstance(), modList); + modLoader.classloadModFiles(); + } + + public static ModLoader getModLoader() + { + return modLoader; + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java index c40cd9428..6c5e794cc 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java @@ -60,6 +60,8 @@ public class FMLServiceProvider implements ITransformationService FMLPaths.setup(environment); fmlLog.debug(CORE,"Loading configuration"); FMLConfig.load(); + fmlLog.debug(CORE, "Preparing launch handler"); + FMLLoader.setupLaunchHandler(environment); fmlLog.debug(CORE,"Initiating mod scan"); FMLLoader.beginModScan(); fmlLog.debug(CORE, "Loading access transformers"); diff --git a/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java index cceb90334..be2d9e4b9 100644 --- a/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java @@ -40,8 +40,6 @@ public interface IModLanguageProvider Consumer getFileVisitor(); interface IModLanguageLoader { - ModContainer loadMod(ModFile file, ClassLoader modClassLoader); + ModContainer loadMod(ModInfo info, ClassLoader modClassLoader); } - - List buildModContainers(List modFiles, ClassLoader modClassLoader); } diff --git a/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java b/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java index 5b7c7f9d1..683d600f7 100644 --- a/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java @@ -19,6 +19,10 @@ package net.minecraftforge.fml.loading; +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.common.versioning.VersionRange; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import java.nio.file.Path; @@ -36,18 +40,41 @@ public class LanguageLoadingProvider { private final List languageProviders = new ArrayList<>(); private final ServiceLoader serviceLoader; - private final Map languageProviderMap = new HashMap<>(); + private final Map languageProviderMap = new HashMap<>(); + private static class ModLanguageWrapper { + private final IModLanguageProvider modLanguageProvider; + private final ArtifactVersion version; + + public ModLanguageWrapper(IModLanguageProvider modLanguageProvider, ArtifactVersion version) + { + this.modLanguageProvider = modLanguageProvider; + this.version = version; + } + + public ArtifactVersion getVersion() + { + return version; + } + + public IModLanguageProvider getModLanguageProvider() + { + return modLanguageProvider; + } + } LanguageLoadingProvider() { serviceLoader = ServiceLoader.load(IModLanguageProvider.class); serviceLoader.forEach(languageProviders::add); languageProviders.forEach(lp -> { final Package pkg = lp.getClass().getPackage(); - fmlLog.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), pkg.getImplementationVersion()); + String implementationVersion = pkg.getImplementationVersion(); + if (implementationVersion == null) { + implementationVersion = ForgeVersion.getVersion(); + } + fmlLog.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), implementationVersion); + languageProviderMap.put(lp.name(), new ModLanguageWrapper(lp, new DefaultArtifactVersion(implementationVersion))); }); - - languageProviders.forEach(lp->languageProviderMap.put(lp.name(), lp)); } public void addAdditionalLanguages(List modFiles) @@ -57,8 +84,15 @@ public class LanguageLoadingProvider serviceLoader.reload(); } - public IModLanguageProvider getLanguage(String name) - { - return languageProviderMap.get(name); + public IModLanguageProvider findLanguage(String modLoader, VersionRange modLoaderVersion) { + final ModLanguageWrapper mlw = languageProviderMap.get(modLoader); + if (mlw == null) { + throw new MissingLanguageException("Missing language "+modLoader); + } + if (!modLoaderVersion.containsVersion(mlw.getVersion())) { + throw new MissingLanguageException("Missing language "+ modLoader + " matching range "+modLoaderVersion + " found "+mlw.getVersion()); + } + + return mlw.getModLanguageProvider(); } } diff --git a/src/main/java/net/minecraftforge/fml/loading/MissingLanguageException.java b/src/main/java/net/minecraftforge/fml/loading/MissingLanguageException.java new file mode 100644 index 000000000..4bbf71c5a --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/MissingLanguageException.java @@ -0,0 +1,28 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading; + +public class MissingLanguageException extends RuntimeException +{ + public MissingLanguageException(String message) + { + super(message); + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/ModList.java b/src/main/java/net/minecraftforge/fml/loading/ModList.java new file mode 100644 index 000000000..0bf83372e --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/ModList.java @@ -0,0 +1,77 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading; + +import com.google.common.collect.Multimap; +import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Master list of all mods + */ +public class ModList +{ + private final List modFiles; + private final List sortedList; + private BackgroundScanHandler scanner; + + public ModList(final List modFiles, final List sortedList) + { + this.modFiles = modFiles; + this.sortedList = sortedList; + } + + public void addCoreMods() + { + modFiles.stream().map(ModFile::getCoreMods).flatMap(List::stream).forEach(FMLLoader.getCoreModProvider()::addCoreMod); + } + + public void addAccessTransformers() + { + modFiles.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod))); + } + + public void addForScanning(BackgroundScanHandler backgroundScanHandler) + { + this.scanner = backgroundScanHandler; + backgroundScanHandler.setModList(this); + modFiles.forEach(backgroundScanHandler::submitForScanning); + } + + public List getModFiles() + { + return modFiles; + } + + public Path findResource(final String className) + { + for (final ModFile mf : modFiles) { + final Path resource = mf.findResource(className); + if (resource != null) return resource; + } + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/ModLoader.java b/src/main/java/net/minecraftforge/fml/loading/ModLoader.java new file mode 100644 index 000000000..fd09e277b --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/ModLoader.java @@ -0,0 +1,54 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading; + +import net.minecraftforge.fml.common.ModContainer; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class ModLoader +{ + private final ClassLoader launchClassLoader; + private final ModList modList; + private final ModLoadingClassLoader modClassLoader; + + public ModLoader(final ClassLoader launchClassLoader, final ModList modList) + { + this.launchClassLoader = launchClassLoader; + this.modList = modList; + this.modClassLoader = new ModLoadingClassLoader(this, this.launchClassLoader); + } + + public void classloadModFiles() + { + + } + + public ModList getModList() + { + return modList; + } + + public void loadMods() { + final List collect = modList.getModFiles().stream().map(mf -> mf.buildMods(this.modClassLoader)).flatMap(Collection::stream).collect(Collectors.toList()); + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/ModLoadingClassLoader.java b/src/main/java/net/minecraftforge/fml/loading/ModLoadingClassLoader.java new file mode 100644 index 000000000..7a02ad402 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/ModLoadingClassLoader.java @@ -0,0 +1,96 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading; + +import org.apache.logging.log4j.LogManager; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureClassLoader; + +import static net.minecraftforge.fml.Logging.LOADING; + +public class ModLoadingClassLoader extends SecureClassLoader +{ + static { + ClassLoader.registerAsParallelCapable(); + // We use a custom URL format : modjar:modid!/path/in/jarfile + URL.setURLStreamHandlerFactory(protocol -> "modjar".equals(protocol) ? new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL url) throws IOException { + return new URLConnection(url) { + @Override + public void connect() + { + url.get + } + }; + } + + @Override + protected void parseURL(URL u, String spec, int start, int limit) + { + + } + } : null); + } + + private ModLoader modLoader; + + protected ModLoadingClassLoader(ModLoader modLoader, ClassLoader parent) { + super(parent); + this.modLoader = modLoader; + } + + @Override + public URL getResource(String name) + { + return super.getResource(name); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + LogManager.getLogger("FML").debug(LOADING, "Loading class {}", name); + final String className = name.replace('.','/').concat(".class"); + final Path classResource = modLoader.getModList().findResource(className); + if (classResource != null) { + try { + final byte[] bytes = Files.readAllBytes(classResource); + return defineClass(name, bytes, 0, bytes.length); + } + catch (IOException e) + { + throw new ClassNotFoundException("Failed to load class file " + classResource + " for "+ className, e); + } + } + throw new ClassNotFoundException("Failed to find class file "+ className); + } + + @Override + protected URL findResource(String name) + { + return super.findResource(name); + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/ModSorter.java b/src/main/java/net/minecraftforge/fml/loading/ModSorter.java new file mode 100644 index 000000000..3b5ebee5e --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/ModSorter.java @@ -0,0 +1,112 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading; + +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.fml.Java9BackportUtils; +import net.minecraftforge.fml.Logging; +import net.minecraftforge.fml.common.DuplicateModsFoundException; +import net.minecraftforge.fml.common.MissingModsException; +import net.minecraftforge.fml.common.toposort.TopologicalSort; +import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import org.apache.logging.log4j.LogManager; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static net.minecraftforge.fml.Logging.LOADING; + +public class ModSorter +{ + private final List modFiles; + private List sortedList; + private Map modIdNameLookup; + + public ModSorter(final List modFiles) + { + this.modFiles = modFiles; + } + + public static ModList sort(List mods) + { + final ModSorter ms = new ModSorter(mods); + ms.buildUniqueList(); + ms.verifyDependencyVersions(); + ms.sort(); + return new ModList(ms.modFiles, ms.sortedList); + } + + private void sort() { + final TopologicalSort.DirectedGraph topoGraph = new TopologicalSort.DirectedGraph<>(); + modFiles.stream().map(ModFile::getModInfos). + flatMap(Collection::stream).forEach(topoGraph::addNode); + modFiles.stream().map(ModFile::getModInfos). + flatMap(Collection::stream).map(ModInfo::getDependencies).flatMap(Collection::stream).forEach(dep->addDependency(topoGraph, dep)); + } + + private void addDependency(TopologicalSort.DirectedGraph topoGraph, ModInfo.ModVersion dep) + { + } + + private void buildUniqueList() + { + final Stream modInfos = modFiles.stream().map(ModFile::getModInfos).flatMap(Collection::stream); + final Map> modIds = modInfos.collect(Collectors.groupingBy(ModInfo::getModId)); + + // TODO: make this figure out dupe handling better + final List>> dupedMods = modIds.entrySet().stream().filter(e -> e.getValue().size() > 1).collect(Collectors.toList()); + + if (!dupedMods.isEmpty()) { + throw new DuplicateModsFoundException(null); + } + + modIdNameLookup = modIds.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0))); + } + + private void verifyDependencyVersions() + { + final Map modVersions = Stream.concat(modFiles.stream().map(ModFile::getModInfos). + flatMap(Collection::stream), ForgeVersion.getModInfos().stream()).collect(Collectors.toMap(ModInfo::getModId, ModInfo::getVersion)); + final Map> modVersionDependencies = modFiles.stream(). + map(ModFile::getModInfos).flatMap(Collection::stream). + collect(Collectors.groupingBy(Function.identity(), Java9BackportUtils.flatMapping(e -> e.getDependencies().stream(), Collectors.toList()))); + final Set mandatoryModVersions = modVersionDependencies.values().stream().flatMap(Collection::stream). + filter(mv -> mv.isMandatory() && mv.getSide().isCorrectSide()).collect(Collectors.toSet()); + LogManager.getLogger("FML").debug(LOADING, "Found {} mandatory requirements", mandatoryModVersions.size()); + final Set missingVersions = mandatoryModVersions.stream().filter(mv->this.modVersionMatches(mv, modVersions)).collect(Collectors.toSet()); + LogManager.getLogger("FML").debug(LOADING, "Found {} mandatory mod requirements missing", missingVersions.size()); + + if (!missingVersions.isEmpty()) { + throw new MissingModsException(null,null,null); + } + } + + private boolean modVersionMatches(final ModInfo.ModVersion mv, final Map modVersions) + { + return !modVersions.containsKey(mv.getModId()) || !mv.getVersionRange().containsVersion(modVersions.get(mv.getModId())); + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java index 42a754f99..fff8444b9 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java @@ -20,6 +20,7 @@ package net.minecraftforge.fml.loading.moddiscovery; import com.google.common.util.concurrent.MoreExecutors; +import net.minecraftforge.fml.loading.ModList; import java.util.ArrayList; import java.util.List; @@ -38,6 +39,7 @@ public class BackgroundScanHandler private final List pendingFiles; private final List scannedFiles; private final List allFiles; + private ModList modList; public BackgroundScanHandler() { modContentScanner = Executors.newSingleThreadExecutor(r -> { @@ -84,4 +86,14 @@ public class BackgroundScanHandler public List getAllFiles() { return allFiles; } + + public void setModList(ModList modList) + { + this.modList = modList; + } + + public ModList getModList() + { + return modList; + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/InvalidModFileException.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/InvalidModFileException.java new file mode 100644 index 000000000..53484a4b3 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/InvalidModFileException.java @@ -0,0 +1,31 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.fml.loading.moddiscovery; + +public class InvalidModFileException extends RuntimeException +{ + private final ModFile.ModFileInfo modFileInfo; + + public InvalidModFileException(String message, ModFile.ModFileInfo modFileInfo) + { + super(message); + this.modFileInfo = modFileInfo; + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java index 931d9ad0d..1ac5efed7 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java @@ -21,13 +21,13 @@ package net.minecraftforge.fml.loading.moddiscovery; import cpw.mods.modlauncher.ServiceLoaderStreamUtils; import net.minecraftforge.fml.loading.FMLLoader; -import net.minecraftforge.fml.loading.LanguageLoadingProvider; +import net.minecraftforge.fml.loading.ModList; +import net.minecraftforge.fml.loading.ModSorter; -import java.nio.file.Path; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ServiceLoader; import java.util.stream.Collectors; @@ -61,11 +61,19 @@ public class ModDiscoverer { FMLLoader.getLanguageLoadingProvider().addAdditionalLanguages(modFiles.get(ModFile.Type.LANGPROVIDER)); BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler(); final List mods = modFiles.get(ModFile.Type.MOD); - mods.forEach(ModFile::identifyMods); + for (Iterator iterator = mods.iterator(); iterator.hasNext(); ) + { + ModFile mod = iterator.next(); + if (!mod.identifyMods()) { + fmlLog.debug(SCAN, "Removing file {} from further analysis - it is invalid", mod); + iterator.remove(); + } + } fmlLog.debug(SCAN,"Found {} mod files with {} mods", mods::size, ()->mods.stream().mapToInt(mf -> mf.getModInfos().size()).sum()); - mods.stream().map(ModFile::getCoreMods).flatMap(List::stream).forEach(FMLLoader.getCoreModProvider()::addCoreMod); - mods.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod))); - mods.forEach(backgroundScanHandler::submitForScanning); + final ModList sortedMods = ModSorter.sort(mods); + sortedMods.addCoreMods(); + sortedMods.addAccessTransformers(); + sortedMods.addForScanning(backgroundScanHandler); return backgroundScanHandler; } } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java index 67659a7c6..034cf0243 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java @@ -19,11 +19,24 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import com.electronwill.nightconfig.core.file.FileConfig; +import com.google.common.collect.ImmutableMap; +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.fml.StringUtils; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.common.versioning.VersionRange; +import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.IModLanguageProvider; +import net.minecraftforge.fml.loading.ModLoadingClassLoader; -import javax.swing.text.html.Option; +import java.net.MalformedURLException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -32,6 +45,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.stream.Collectors; @@ -48,10 +62,29 @@ public class ModFile DEFAULTMANIFEST.getMainAttributes().putValue("FMLModType", "MOD"); } + private final String jarVersion; + private Map fileProperties; + private IModLanguageProvider loader; - public void claimLanguage(String modId, IModLanguageProvider.IModLanguageLoader loader) + public void setFileProperties(Map fileProperties) { - this.modInfoMap.get(modId).setLoader(loader); + this.fileProperties = fileProperties; + } + + public IModLanguageProvider getLoader() + { + return loader; + } + + public Path findResource(String className) + { + return locator.findPath(this, className); + } + + public List buildMods(ModLoadingClassLoader modClassLoader) + { + return getScanResult().getTargets().entrySet().stream(). + map(e->e.getValue().loadMod(this.modInfoMap.get(e.getKey()), modClassLoader)).collect(Collectors.toList()); } public enum Type { @@ -60,9 +93,9 @@ public class ModFile private final Path filePath; private final Type modFileType; private final Manifest manifest; - private List modInfos; - private Map modInfoMap; private final IModLocator locator; + private ModFileInfo modFileInfo; + private Map modInfoMap; private ScanResult fileScanResult; private CompletableFuture futureScanResult; private List coreMods; @@ -78,8 +111,12 @@ public class ModFile else fmlLog.debug(SCAN,"Mod file {} is missing a manifest", file); final Optional value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE)); modFileType = Type.valueOf(value.orElse("MOD")); + jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("NONE"); } + public Supplier> getSubstitutionMap() { + return () -> ImmutableMap.builder().put("jarVersion", jarVersion).putAll(fileProperties).build(); + } public Type getType() { return modFileType; } @@ -89,19 +126,22 @@ public class ModFile } public List getModInfos() { - return modInfos; + return modFileInfo.getMods(); } public Optional getAccessTransformer() { return Optional.ofNullable(Files.exists(accessTransformer) ? accessTransformer : null); } - public void identifyMods() { - this.modInfos = ModFileParser.readModList(this); - this.modInfos.forEach(mi-> fmlLog.debug(LOADING,"Found mod {} for language {}", mi.getModId(), mi.getModLoader())); - this.modInfoMap = this.modInfos.stream().collect(Collectors.toMap(ModInfo::getModId, Function.identity())); + public boolean identifyMods() { + this.modFileInfo = ModFileParser.readModList(this); + if (this.modFileInfo == null) return false; + fmlLog.debug(LOADING,"Loading mod file {} with language {}", this.getFilePath(), this.modFileInfo.modLoader); + this.modInfoMap = this.getModInfos().stream().collect(Collectors.toMap(ModInfo::getModId, Function.identity())); this.coreMods = ModFileParser.getCoreMods(this); this.coreMods.forEach(mi-> fmlLog.debug(LOADING,"Found coremod {}", mi.getPath())); this.accessTransformer = locator.findPath(this, "META-INF", "accesstransformer.cfg"); + this.loader = FMLLoader.getLanguageLoadingProvider().findLanguage(this.modFileInfo.modLoader, this.modFileInfo.modLoaderVersion); + return true; } @@ -152,4 +192,54 @@ public class ModFile public IModLocator getLocator() { return locator; } + + public ModFileInfo getModFileInfo() { + return modFileInfo; + } + public static class ModFileInfo { + private final UnmodifiableConfig config; + private final ModFile modFile; + private final URL updateJSONURL; + private final URL issueURL; + private final String modLoader; + private final VersionRange modLoaderVersion; + private final List mods; + private final Map properties; + + public ModFileInfo(final ModFile modFile, final UnmodifiableConfig config) + { + this.modFile = modFile; + this.config = config; + this.modLoader = config.getOptional("modLoader"). + orElseThrow(()->new InvalidModFileException("Missing ModLoader in file", this)); + this.modLoaderVersion = config.getOptional("loaderVersion"). + map(VersionRange::createFromVersionSpec). + orElseThrow(()->new InvalidModFileException("Missing ModLoader version in file", this)); + this.properties = config.getOptional("properties"). + map(UnmodifiableConfig::valueMap).orElse(Collections.emptyMap()); + this.modFile.setFileProperties(this.properties); + final ArrayList modConfigs = config.getOrElse("mods", ArrayList::new); + if (modConfigs.isEmpty()) { + throw new InvalidModFileException("Missing mods list", this); + } + this.mods = modConfigs.stream().map(mi-> new ModInfo(this, mi)).collect(Collectors.toList()); + this.updateJSONURL = config.getOptional("updateJSONURL").map(StringUtils::toURL).orElse(null); + this.issueURL = config.getOptional("issueTrackerURL").map(StringUtils::toURL).orElse(null); + } + + public List getMods() + { + return mods; + } + + public ModFile getFile() + { + return this.modFile; + } + + public UnmodifiableConfig getConfig() + { + return this.config; + } + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFileParser.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFileParser.java index 1bba72e2a..994cfd37d 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFileParser.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFileParser.java @@ -19,13 +19,10 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.electronwill.nightconfig.core.path.PathConfig; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.InstanceCreator; -import com.google.gson.JsonDeserializer; import com.google.gson.reflect.TypeToken; -import net.minecraftforge.fml.common.versioning.ArtifactVersion; -import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import org.apache.commons.lang3.text.StrSubstitutor; import java.io.IOException; import java.lang.reflect.Type; @@ -35,26 +32,27 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.fmlLog; public class ModFileParser { - protected static List readModList(final ModFile modFile) { + public static ModFile.ModFileInfo readModList(final ModFile modFile) { fmlLog.debug(LOADING,"Parsing mod file candidate {}", modFile.getFilePath()); - try { - final Path modsjson = modFile.getLocator().findPath(modFile, "META-INF", "mods.json"); - GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(ModInfo.class, (InstanceCreator)ic -> new ModInfo(modFile, null, null, null, null, null, null, null)); - gsonBuilder.registerTypeAdapter(ArtifactVersion.class, (JsonDeserializer) (element, type, context) -> new DefaultArtifactVersion(element.getAsString())); - Gson gson = gsonBuilder.create(); - final ModInfo[] modInfos = gson.fromJson(Files.newBufferedReader(modsjson), ModInfo[].class); - return Stream.of(modInfos).collect(Collectors.toList()); - } catch (IOException e) { - fmlLog.debug(LOADING,"Ignoring invalid JAR file {}", modFile.getFilePath()); - return Collections.emptyList(); + final Path modsjson = modFile.getLocator().findPath(modFile, "META-INF", "mods.toml"); + if (!Files.exists(modsjson)) { + fmlLog.warn(LOADING, "Mod file {} is missing mods.toml file", modFile); + return null; } + return loadModFile(modFile, modsjson); + } + + public static ModFile.ModFileInfo loadModFile(final ModFile file, final Path modsjson) + { + final PathConfig pathConfig = PathConfig.builder(modsjson).build(); + pathConfig.load(); + pathConfig.close(); + return new ModFile.ModFileInfo(file, pathConfig); } protected static List getCoreMods(final ModFile modFile) { diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java index 7910600c8..df1ecedd1 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java @@ -19,40 +19,60 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import net.minecraftforge.fml.FMLEnvironment; +import net.minecraftforge.fml.StringSubstitutor; +import net.minecraftforge.fml.StringUtils; import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.common.versioning.VersionRange; import net.minecraftforge.fml.loading.IModLanguageProvider; -import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; public class ModInfo { - private final ModFile owningFile; + private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1"); + public static final VersionRange UNBOUNDED = VersionRange.createFromVersionSpec(""); + private final ModFile.ModFileInfo owningFile; private final String modId; private final ArtifactVersion version; private final String displayName; private final String description; - private final URL updateJSONURL; - private final String modLoader; private final List dependencies; - private IModLanguageProvider.IModLanguageLoader loader; + private final Map properties; + private final UnmodifiableConfig modConfig; - public ModInfo(final ModFile owningFile, final String modLoader, final String modId, final String displayName, final ArtifactVersion version, final String description, final URL updateJSONURL, final List dependencies) { + public ModInfo(final ModFile.ModFileInfo owningFile, final UnmodifiableConfig modConfig) + { this.owningFile = owningFile; - this.modLoader = modLoader; - this.modId = modId; - this.displayName = displayName; - this.version = version; - this.description = description; - this.updateJSONURL = updateJSONURL; - this.dependencies = dependencies; + this.modConfig = modConfig; + this.modId = modConfig.getOptional("modId").orElseThrow(() -> new InvalidModFileException("Missing modId entry", owningFile)); + this.version = modConfig.getOptional("version"). + map(s->StringSubstitutor.replace(s, owningFile != null ? owningFile.getFile() : null )). + map(DefaultArtifactVersion::new).orElse(DEFAULT_VERSION); + this.displayName = modConfig.getOptional("displayName").orElse(null); + this.description = modConfig.get("description"); + if (owningFile != null) { + this.dependencies = owningFile.getConfig().>getOptional(Arrays.asList("dependencies", this.modId)). + orElse(Collections.emptyList()).stream().map(dep -> new ModVersion(this, dep)).collect(Collectors.toList()); + this.properties = owningFile.getConfig().getOptional(Arrays.asList("modproperties", this.modId)). + map(UnmodifiableConfig::valueMap).orElse(Collections.emptyMap()); + } else { + this.dependencies = Collections.emptyList(); + this.properties = Collections.emptyMap(); + } + } public ModFile getOwningFile() { - return owningFile; - } - - public String getModLoader() { - return modLoader; + return owningFile.getFile(); } public String getModId() { @@ -63,32 +83,72 @@ public class ModInfo { return version; } - public void setLoader(IModLanguageProvider.IModLanguageLoader loader) - { - this.loader = loader; + public List getDependencies() { + return this.dependencies; } public enum Ordering { - BEFORE, AFTER, NONE; + BEFORE, AFTER, NONE } public enum DependencySide { CLIENT, SERVER, BOTH; + + public boolean isCorrectSide() + { + return this == BOTH || FMLEnvironment.side.name().equals(this.name()); + } } public static class ModVersion { + private ModInfo owner; private final String modId; - private final ArtifactVersion version; + private final VersionRange versionRange; private final boolean mandatory; private final Ordering ordering; private final DependencySide side; - public ModVersion(final String modId, final ArtifactVersion version, final boolean mandatory, final Ordering ordering, final DependencySide side) { - this.modId = modId; - this.version = version; - this.mandatory = mandatory; - this.ordering = ordering; - this.side = side; + ModVersion(final ModInfo owner, final UnmodifiableConfig config) { + this.owner = owner; + this.modId = config.get("modId"); + this.versionRange = config.getOptional("versionRange").map(String.class::cast). + map(VersionRange::createFromVersionSpec).orElse(UNBOUNDED); + this.mandatory = config.get("mandatory"); + this.ordering = config.getOptional("ordering").map(String.class::cast). + map(Ordering::valueOf).orElse(Ordering.NONE); + this.side = config.getOptional("side").map(String.class::cast). + map(DependencySide::valueOf).orElse(DependencySide.BOTH); + } + + + public String getModId() + { + return modId; + } + + public VersionRange getVersionRange() + { + return versionRange; + } + + public boolean isMandatory() + { + return mandatory; + } + + public Ordering getOrdering() + { + return ordering; + } + + public DependencySide getSide() + { + return side; + } + + public void setOwner(final ModInfo owner) + { + this.owner = owner; } } } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java index cd75eaa90..1a75c8c21 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java @@ -20,6 +20,9 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.google.common.collect.Multimap; +import net.minecraftforge.fml.loading.FMLJavaModLanguageProvider; +import net.minecraftforge.fml.loading.IModLanguageProvider; import org.objectweb.asm.Type; import java.util.ArrayList; @@ -31,6 +34,7 @@ public class ScanResult { private final ModFile file; private final List annotations = new ArrayList<>(); private final List classes = new ArrayList<>(); + private Map modTargets; public ScanResult(final ModFile file) { this.file = file; @@ -52,6 +56,17 @@ public class ScanResult { return annotations; } + public void addLanguageLoader(Map modTargetMap) + { + modTargets = modTargetMap; + } + + public Map getTargets() + { + return modTargets; + } + + public static class ClassData { private final Type clazz; private final Type parent; diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java index 92fdab37d..7001cb2b2 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java @@ -45,9 +45,9 @@ public class Scanner { public ScanResult scan() { ScanResult result = new ScanResult(fileToScan); fileToScan.scanFile(p -> fileVisitor(p, result)); - final Set modLoaders = fileToScan.getModInfos().stream().map(ModInfo::getModLoader).map(FMLLoader.getLanguageLoadingProvider()::getLanguage).collect(Collectors.toSet()); - modLoaders.stream().peek(ml-> fmlLog.debug(SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), ml.name())) - .map(IModLanguageProvider::getFileVisitor).forEach(c->c.accept(result)); + final IModLanguageProvider loader = fileToScan.getLoader(); + fmlLog.debug(SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name()); + loader.getFileVisitor().accept(result); return result; } diff --git a/src/main/java/net/minecraftforge/logging/ModelLoaderErrorMessage.java b/src/main/java/net/minecraftforge/logging/ModelLoaderErrorMessage.java new file mode 100644 index 000000000..4cd6c6de2 --- /dev/null +++ b/src/main/java/net/minecraftforge/logging/ModelLoaderErrorMessage.java @@ -0,0 +1,128 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * 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 version 2.1 + * of the License. + * + * 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. + * + * 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 + */ + +package net.minecraftforge.logging; + +import com.google.common.base.Joiner; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.IRegistry; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import org.apache.logging.log4j.message.SimpleMessage; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Function; + +import static net.minecraftforge.client.model.ModelLoader.getInventoryVariant; + +public class ModelLoaderErrorMessage extends SimpleMessage +{ + private final ModelResourceLocation resourceLocation; + private final Exception exception; + private final IRegistry modelRegistry; + + private static Multimap reverseBlockMap = HashMultimap.create(); + private static Multimap reverseItemMap = HashMultimap.create(); + + private static void buildLookups(final BlockModelShapes blockModelShapes, Function> itemNameLookup) { + if (!reverseBlockMap.isEmpty()) return; + for(Map.Entry entry : blockModelShapes.getBlockStateMapper().putAllStateModelLocations().entrySet()) + { + reverseBlockMap.put(entry.getValue(), entry.getKey()); + } + ForgeRegistries.ITEMS.forEach(item -> + { + for(String s : itemNameLookup.apply(item)) + { + ModelResourceLocation memory = getInventoryVariant(s); + reverseItemMap.put(memory, item.getRegistryName().toString()); + } + }); + + } + + public ModelLoaderErrorMessage(ModelResourceLocation resourceLocation, Exception exception, IRegistry modelRegistry, BlockModelShapes blockModelShapes, Function> itemNameLookup) + { + // if we're logging these error messages, this will get built for reference + buildLookups(blockModelShapes, itemNameLookup); + this.resourceLocation = resourceLocation; + this.exception = exception; + this.modelRegistry = modelRegistry; + } + + private void stuffs() { + String domain = resourceLocation.getResourceDomain(); + String errorMsg = "Exception loading model for variant " + resourceLocation; + Collection blocks = reverseBlockMap.get(resourceLocation); + if(!blocks.isEmpty()) + { + if(blocks.size() == 1) + { + errorMsg += " for blockstate \"" + blocks.iterator().next() + "\""; + } + else + { + errorMsg += " for blockstates [\"" + Joiner.on("\", \"").join(blocks) + "\"]"; + } + } + Collection items = reverseItemMap.get(resourceLocation); + if(!items.isEmpty()) + { + if(!blocks.isEmpty()) errorMsg += " and"; + if(items.size() == 1) + { + errorMsg += " for item \"" + items.iterator().next() + "\""; + } + else + { + errorMsg += " for items [\"" + Joiner.on("\", \"").join(items) + "\"]"; + } + } + if(exception instanceof ModelLoader.ItemLoadingException) + { + ModelLoader.ItemLoadingException ex = (ModelLoader.ItemLoadingException)exception; +// FMLLog.log.error("{}, normal location exception: ", errorMsg, ex.normalException); +// FMLLog.log.error("{}, blockstate location exception: ", errorMsg, ex.blockstateException); + } + else + { +// FMLLog.log.error(errorMsg, entry.getValue()); + } +// ResourceLocation blockstateLocation = new ResourceLocation(resourceLocation.getResourceDomain(), resourceLocation.getResourcePath()); +// if(loadingExceptions.containsKey(blockstateLocation) && !printedBlockStateErrors.contains(blockstateLocation)) +// { +// FMLLog.log.error("Exception loading blockstate for the variant {}: ", location, loadingExceptions.get(blockstateLocation)); +// printedBlockStateErrors.add(blockstateLocation); +// } + } + @Override + public void formatTo(StringBuilder buffer) + { + + } +} diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 223634728..c6693377b 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -30,6 +30,7 @@ import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import net.minecraftforge.fml.ModThreadContext; import org.apache.commons.lang3.Validate; import java.util.Set; @@ -277,8 +278,7 @@ public class ForgeRegistry> implements IForgeRe int add(int id, V value) { - ModContainer mc = Loader.instance().activeModContainer(); - String owner = mc == null || (mc instanceof InjectedModContainer && ((InjectedModContainer)mc).wrappedContainer instanceof FMLContainer) ? null : mc.getModId().toLowerCase(); + final String owner = ModThreadContext.get().getCurrentContainer().getPrefix(); return add(id, value, owner); } @@ -430,7 +430,7 @@ public class ForgeRegistry> implements IForgeRe { try { - ReflectionHelper.findMethod(BitSet.class, "trimToSize", null).invoke(this.availabilityMap); +// ReflectionHelper.findMethod(BitSet.class, "trimToSize", null).invoke(this.availabilityMap); } catch (Exception e) { diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 1f0e3476e..03d6b5fd7 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -49,6 +49,7 @@ import net.minecraft.world.biome.Biome; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent.MissingMappings; +import net.minecraftforge.fml.Logging; import net.minecraftforge.fml.common.EnhancedRuntimeException; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.FMLContainer; @@ -83,6 +84,8 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.Level; +import static net.minecraftforge.fml.Logging.fmlLog; + /** * INTERNAL ONLY * MODDERS SHOULD HAVE NO REASON TO USE THIS CLASS @@ -201,7 +204,7 @@ public class GameData @SuppressWarnings({ "unchecked", "rawtypes" }) public static void vanillaSnapshot() { - FMLLog.log.debug("Creating vanilla freeze snapshot"); + fmlLog.debug("Creating vanilla freeze snapshot"); for (Map.Entry>> r : RegistryManager.ACTIVE.registries.entrySet()) { final Class clazz = RegistryManager.ACTIVE.getSuperType(r.getKey()); @@ -214,7 +217,7 @@ public class GameData }); RegistryManager.VANILLA.registries.forEach(LOCK_VANILLA); RegistryManager.ACTIVE.registries.forEach(LOCK_VANILLA); - FMLLog.log.debug("Vanilla freeze snapshot created"); + fmlLog.debug("Vanilla freeze snapshot created"); } @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService b/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService index 4d7e89883..0ee4f188f 100644 --- a/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService +++ b/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService @@ -1,2 +1,2 @@ net.minecraftforge.fml.loading.FMLLaunchProvider -net.minecraftforge.fml.loading.FMLDevLaunchProvider \ No newline at end of file +net.minecraftforge.fml.loading.FMLDevClientLaunchProvider \ No newline at end of file diff --git a/src/main/resources/forgemod.toml b/src/main/resources/forgemod.toml new file mode 100644 index 000000000..ad1ece3f8 --- /dev/null +++ b/src/main/resources/forgemod.toml @@ -0,0 +1,6 @@ +modId="forge" +version="${global.forgeVersion}" +displayName="Forge" +description=''' +Forge, the magic +''' \ No newline at end of file diff --git a/src/main/resources/minecraftmod.toml b/src/main/resources/minecraftmod.toml new file mode 100644 index 000000000..53bd08c2d --- /dev/null +++ b/src/main/resources/minecraftmod.toml @@ -0,0 +1,6 @@ +modId="minecraft" +version="${global.mcVersion}" +displayName="Minecraft" +description=''' +Minecraft, a modded story +''' \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/fml/test/ModsTomlTest.java b/src/test/java/net/minecraftforge/fml/test/ModsTomlTest.java new file mode 100644 index 000000000..94e99790e --- /dev/null +++ b/src/test/java/net/minecraftforge/fml/test/ModsTomlTest.java @@ -0,0 +1,37 @@ +package net.minecraftforge.fml.test; + +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModFileParser; +import org.junit.Test; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.jar.JarFile; + +public class ModsTomlTest +{ + @Test + public void testTomlLoad() throws URISyntaxException + { + final URL resource = getClass().getClassLoader().getResource("mods.toml"); + final Path path = Paths.get(resource.toURI()); + final ModFile.ModFileInfo modFileInfo = ModFileParser.loadModFile(null, path); + modFileInfo.getMods(); + } + + @Test + public void testTomlInJar() throws URISyntaxException, IOException + { + final URL resource = getClass().getClassLoader().getResource("mod.jar"); + + final FileSystem fileSystem = FileSystems.newFileSystem(Paths.get(resource.toURI()), getClass().getClassLoader()); + final Path path = fileSystem.getPath("META-INF", "mods.toml"); + ModFileParser.loadModFile(null, path); + } +} diff --git a/src/test/resources/blah.json b/src/test/resources/blah.json new file mode 100644 index 000000000..a02868303 --- /dev/null +++ b/src/test/resources/blah.json @@ -0,0 +1,5 @@ +{ + "reply": { + "message": "no" + } +} \ No newline at end of file diff --git a/src/test/resources/mods.toml b/src/test/resources/mods.toml new file mode 100644 index 000000000..11d1f6da9 --- /dev/null +++ b/src/test/resources/mods.toml @@ -0,0 +1,71 @@ +# The name of the mod loader type to load +modLoader="javafml" +# A version range to match for said mod loader +loaderVersion="[2.0,)" +# A URL to query for updates +updateJSONURL="http://myurl.me/" +# A URL to refer people to when problems occur with this mod +issueTrackerURL="http://my.issue.tracker/" +# Extra mod loader property +randomScalaProperty="fishy" +# Arbitrary key-value property pairs available to mods +[properties] +key="value" + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] + # The modid of the mod + modId="inventorysorter" + # The version number of the mod + version="1.1" + # A display name for the mod + displayName="Inventory Sorter" + # The description text for the mod (multi line!) + description=''' + This is my mod, there may be + others, but this one is mine + ''' + # A random extra property for a mod loader + randomExtraProperty="somevalue" + # Arbitrary key-value pairs + [modproperties.inventorysorter] + key="value" + # A list of dependencies + [[dependencies.inventorysorter]] + # the modid of the dependency + modId="forge" + # Does this dependency have to exist - if not, ordering below must be specified + mandatory=true + # The version range of the dependency + versionRange="[14.23.2.0,)" + # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side="BOTH" + # Here's another dependency + [[dependencies.inventorysorter]] + modId="minecraft" + mandatory=true + versionRange="[1.12.2]" + ordering="NONE" + side="BOTH" +# Here's another mod in this jar +[[mods]] + # Note that other mod types may want to add additional key-value pairs in here + modId="inventorysortertoo" + # ${jarVersion} will read the Implementation-Version of the jar file + version="${jarVersion}" + displayName="Inventory Sorter as well" + description=''' + This is also my mod, there may be + others, but this one is mine + ''' + [[dependencies.inventorysortertoo]] + modId="minecraft" + mandatory=true + versionRange="[1.12.2]" + ordering="NONE" + side="BOTH" +[[mods]] + # A minimal mod + modId="minimalmod"