Holy Moly, it's a big fat commit of broken code!

This commit is contained in:
cpw 2018-06-06 11:37:56 -04:00 committed by LexManos
parent 5b05e103f4
commit c1f0e1b68f
63 changed files with 2027 additions and 542 deletions

62
mods.toml Normal file
View File

@ -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
'''

View File

@ -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;
}

View File

@ -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<ModelResourceLocation, IModel> 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<ModelResourceLocation, IBakedModel> modelRegistry)
{
if (!isLoading) return;
IBakedModel missingModel = modelRegistry.getObject(MODEL_MISSING);
Map<String, Integer> modelErrors = Maps.newHashMap();
Set<ResourceLocation> printedBlockStateErrors = Sets.newHashSet();
Multimap<ModelResourceLocation, IBlockState> reverseBlockMap = null;
Multimap<ModelResourceLocation, String> reverseItemMap = HashMultimap.create();
if(enableVerboseMissingInfo)
{
reverseBlockMap = HashMultimap.create();
for(Map.Entry<IBlockState, ModelResourceLocation> 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<ResourceLocation, Exception> 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<IBlockState> 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<String> 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<String, Integer> 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;

View File

@ -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<ModInfo> 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(),

View File

@ -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();
}
/**

View File

@ -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<ICrashCallable> 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)
{
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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 <T, U, A, R>
Collector<T, ?, R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return Collector.of(downstream.supplier(),
(r, t) -> {
try (Stream<? extends U> 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]));
}
}

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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<ModThreadContext> context = ThreadLocal.withInitial(ModThreadContext::new);
public static ModThreadContext get() {
return context.get();
}
private ModContainer currentContainer;
public ModContainer getCurrentContainer() {
return currentContainer == null ? DefaultModContainers.MINECRAFT : currentContainer;
}
}

View File

@ -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 <T> The return type from the callable
* @return The callable's result
*/
public static <T> T runOn(Side side, Supplier<Callable<T>> toRun) {
if (side == Side.CLIENT) {
try
{
return toRun.get().call();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
return null;
}
}

View File

@ -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<String,String> 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<String> getStringLookup(final ModFile file) {
return new StrLookup<String>()
{
@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<String> split(final String in) {
return Arrays.asList(in.split("."));
}
}

View File

@ -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<String, String> properties) {
return StrSubstitutor.replace(input, properties);
}
}

View File

@ -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<URL, String> {
@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();
}
}
}

View File

@ -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<String> brandings;
private static List<String> brandingsNoMC;
private static void computeBranding()
{
if (brandings == null)
{
ImmutableList.Builder<String> 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<String> 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<String> defaultClientBranding = Stream.of("fml", "forge").collect(Collectors.toList());
public static String getClientBranding() {
return defaultClientBranding.stream().collect(Collectors.joining(","));
}
}

View File

@ -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<IResourcePack> 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();
}
}

View File

@ -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
{
}

View File

@ -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";

View File

@ -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);
}

View File

@ -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()
{

View File

@ -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<String> brandings;
private List<String> brandingsNoMC;
private List<ICrashCallable> crashCallables = Lists.newArrayList(Loader.instance().getCallableCrashInformation());
private Set<SaveHandler> handlerSet = Collections.newSetFromMap(new MapMaker().weakKeys().<SaveHandler,Boolean>makeMap());
private WeakReference<SaveHandler> 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<String> 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<String> 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<String, String> 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<Object, Object> 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;

View File

@ -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<String, Object> 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<? extends FMLEvent> parameterType = (Class<? extends FMLEvent>) m.getParameterTypes()[0];
eventMethods.put(parameterType, m);
eventMethods.put((Class<? extends FMLEvent>)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<String, ASMData> 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<String> certList = CertificateHelper.getFingerprints(certificates);
sourceFingerprints = ImmutableSet.copyOf(certList);
Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates();
ImmutableList<String> 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<Map<String, Object>> props = (List<Map<String, Object>>)descriptor.get("customProperties");
if (props != null)
{
com.google.common.collect.ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (Map<String, Object> 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<Map<String, String>> props = (List<Map<String, String>>)descriptor.get("customProperties");
if (props != null)
{
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (Map<String, String> 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);
}
}

View File

@ -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<ModContainer> getActiveModList()

View File

@ -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

View File

@ -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

View File

@ -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++;

View File

@ -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<ModContainer> sort()
{
List<ModContainer> sortedList = TopologicalSort.topologicalSort(modGraph);
List<ModContainer> 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;
}

View File

@ -59,7 +59,7 @@ public class ModSortingException extends EnhancedRuntimeException implements IDi
public <T> ModSortingException(String string, T node, Set<T> visitedNodes)
{
super(string);
this.sortingExceptionData = new SortingExceptionData<T>(node, visitedNodes);
this.sortingExceptionData = new SortingExceptionData<>(node, visitedNodes);
}
@SuppressWarnings("unchecked")

View File

@ -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<T> implements Iterable<T>
{
private final Map<T, SortedSet<T>> graph = new HashMap<T, SortedSet<T>>();
private List<T> orderedNodes = new ArrayList<T>();
private final Map<T, SortedSet<T>> graph = new HashMap<>();
private List<T> orderedNodes = new ArrayList<>();
public boolean addNode(T node)
{
@ -60,7 +55,7 @@ public class TopologicalSort
}
orderedNodes.add(node);
graph.put(node, new TreeSet<T>(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 <T> List<T> topologicalSort(DirectedGraph<T> graph)
{
DirectedGraph<T> rGraph = reverse(graph);
List<T> sortedResult = new ArrayList<T>();
Set<T> visitedNodes = new HashSet<T>();
List<T> sortedResult = new ArrayList<>();
Set<T> visitedNodes = new HashSet<>();
// A list of "fully explored" nodes. Leftovers in here indicate cycles in the graph
Set<T> expandedNodes = new HashSet<T>();
Set<T> expandedNodes = new HashSet<>();
for (T node : rGraph)
{
@ -151,7 +146,7 @@ public class TopologicalSort
public static <T> DirectedGraph<T> reverse(DirectedGraph<T> graph)
{
DirectedGraph<T> result = new DirectedGraph<T>();
DirectedGraph<T> result = new DirectedGraph<>();
for (T node : graph)
{
@ -169,7 +164,7 @@ public class TopologicalSort
return result;
}
public static <T> void explore(T node, DirectedGraph<T> graph, List<T> sortedResult, Set<T> visitedNodes, Set<T> expandedNodes)
private static <T> void explore(T node, DirectedGraph<T> graph, List<T> sortedResult, Set<T> visitedNodes, Set<T> 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<T> 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;
}
}
}

View File

@ -27,7 +27,7 @@ package net.minecraftforge.fml.common.versioning;
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public class InvalidVersionSpecificationException extends Exception
public class InvalidVersionSpecificationException extends RuntimeException
{
private static final long serialVersionUID = 1L;

View File

@ -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<String, String> 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<Object, Object> e : props.entrySet())
{
table.put((String)e.getKey(), (String)e.getValue());
}
props.clear();
return null;
}
}

View File

@ -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);
}
}

View File

@ -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<Void> launchService(String[] arguments, ClassLoader launchClassLoader)
public Callable<Void> 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;
}
}

View File

@ -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<ModContainer> buildModContainers(List<ModInfo> modFiles, ClassLoader modClassLoader)
{
return null;
}
}

View File

@ -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<Void> launchService(String[] arguments, ClassLoader launchClassLoader)
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
return null;
};
}
@Override
public Side getSidedness()
{
return Side.CLIENT;
}
}

View File

@ -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<String> 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<ILaunchHandlerService> 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;
}
}

View File

@ -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");

View File

@ -40,8 +40,6 @@ public interface IModLanguageProvider
Consumer<ScanResult> getFileVisitor();
interface IModLanguageLoader {
ModContainer loadMod(ModFile file, ClassLoader modClassLoader);
ModContainer loadMod(ModInfo info, ClassLoader modClassLoader);
}
List<ModContainer> buildModContainers(List<ModInfo> modFiles, ClassLoader modClassLoader);
}

View File

@ -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<IModLanguageProvider> languageProviders = new ArrayList<>();
private final ServiceLoader<IModLanguageProvider> serviceLoader;
private final Map<String, IModLanguageProvider> languageProviderMap = new HashMap<>();
private final Map<String, ModLanguageWrapper> 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<ModFile> 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();
}
}

View File

@ -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);
}
}

View File

@ -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<ModFile> modFiles;
private final List<ModInfo> sortedList;
private BackgroundScanHandler scanner;
public ModList(final List<ModFile> modFiles, final List<ModInfo> 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<ModFile> 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;
}
}

View File

@ -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<ModContainer> collect = modList.getModFiles().stream().map(mf -> mf.buildMods(this.modClassLoader)).flatMap(Collection::stream).collect(Collectors.toList());
}
}

View File

@ -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);
}
}

View File

@ -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<ModFile> modFiles;
private List<ModInfo> sortedList;
private Map<String, ModInfo> modIdNameLookup;
public ModSorter(final List<ModFile> modFiles)
{
this.modFiles = modFiles;
}
public static ModList sort(List<ModFile> 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<ModInfo> 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<ModInfo> topoGraph, ModInfo.ModVersion dep)
{
}
private void buildUniqueList()
{
final Stream<ModInfo> modInfos = modFiles.stream().map(ModFile::getModInfos).flatMap(Collection::stream);
final Map<String, List<ModInfo>> modIds = modInfos.collect(Collectors.groupingBy(ModInfo::getModId));
// TODO: make this figure out dupe handling better
final List<Map.Entry<String, List<ModInfo>>> 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<String, ArtifactVersion> modVersions = Stream.concat(modFiles.stream().map(ModFile::getModInfos).
flatMap(Collection::stream), ForgeVersion.getModInfos().stream()).collect(Collectors.toMap(ModInfo::getModId, ModInfo::getVersion));
final Map<ModInfo, List<ModInfo.ModVersion>> modVersionDependencies = modFiles.stream().
map(ModFile::getModInfos).flatMap(Collection::stream).
collect(Collectors.groupingBy(Function.identity(), Java9BackportUtils.flatMapping(e -> e.getDependencies().stream(), Collectors.toList())));
final Set<ModInfo.ModVersion> 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<ModInfo.ModVersion> 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<String, ArtifactVersion> modVersions)
{
return !modVersions.containsKey(mv.getModId()) || !mv.getVersionRange().containsVersion(modVersions.get(mv.getModId()));
}
}

View File

@ -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<ModFile> pendingFiles;
private final List<ModFile> scannedFiles;
private final List<ModFile> allFiles;
private ModList modList;
public BackgroundScanHandler() {
modContentScanner = Executors.newSingleThreadExecutor(r -> {
@ -84,4 +86,14 @@ public class BackgroundScanHandler
public List<ModFile> getAllFiles() {
return allFiles;
}
public void setModList(ModList modList)
{
this.modList = modList;
}
public ModList getModList()
{
return modList;
}
}

View File

@ -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;
}
}

View File

@ -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<ModFile> mods = modFiles.get(ModFile.Type.MOD);
mods.forEach(ModFile::identifyMods);
for (Iterator<ModFile> 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;
}
}

View File

@ -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<String, Object> fileProperties;
private IModLanguageProvider loader;
public void claimLanguage(String modId, IModLanguageProvider.IModLanguageLoader loader)
public void setFileProperties(Map<String, Object> 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<ModContainer> 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<ModInfo> modInfos;
private Map<String, ModInfo> modInfoMap;
private final IModLocator locator;
private ModFileInfo modFileInfo;
private Map<String, ModInfo> modInfoMap;
private ScanResult fileScanResult;
private CompletableFuture<ScanResult> futureScanResult;
private List<CoreModFile> coreMods;
@ -78,8 +111,12 @@ public class ModFile
else fmlLog.debug(SCAN,"Mod file {} is missing a manifest", file);
final Optional<String> 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<Map<String,Object>> getSubstitutionMap() {
return () -> ImmutableMap.<String,Object>builder().put("jarVersion", jarVersion).putAll(fileProperties).build();
}
public Type getType() {
return modFileType;
}
@ -89,19 +126,22 @@ public class ModFile
}
public List<ModInfo> getModInfos() {
return modInfos;
return modFileInfo.getMods();
}
public Optional<Path> 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<ModInfo> mods;
private final Map<String,Object> properties;
public ModFileInfo(final ModFile modFile, final UnmodifiableConfig config)
{
this.modFile = modFile;
this.config = config;
this.modLoader = config.<String>getOptional("modLoader").
orElseThrow(()->new InvalidModFileException("Missing ModLoader in file", this));
this.modLoaderVersion = config.<String>getOptional("loaderVersion").
map(VersionRange::createFromVersionSpec).
orElseThrow(()->new InvalidModFileException("Missing ModLoader version in file", this));
this.properties = config.<UnmodifiableConfig>getOptional("properties").
map(UnmodifiableConfig::valueMap).orElse(Collections.emptyMap());
this.modFile.setFileProperties(this.properties);
final ArrayList<UnmodifiableConfig> 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.<String>getOptional("updateJSONURL").map(StringUtils::toURL).orElse(null);
this.issueURL = config.<String>getOptional("issueTrackerURL").map(StringUtils::toURL).orElse(null);
}
public List<ModInfo> getMods()
{
return mods;
}
public ModFile getFile()
{
return this.modFile;
}
public UnmodifiableConfig getConfig()
{
return this.config;
}
}
}

View File

@ -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<ModInfo> 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<ModInfo>)ic -> new ModInfo(modFile, null, null, null, null, null, null, null));
gsonBuilder.registerTypeAdapter(ArtifactVersion.class, (JsonDeserializer<ArtifactVersion>) (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<CoreModFile> getCoreMods(final ModFile modFile) {

View File

@ -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<ModInfo.ModVersion> dependencies;
private IModLanguageProvider.IModLanguageLoader loader;
private final Map<String,Object> 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<ModInfo.ModVersion> 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.<String>getOptional("modId").orElseThrow(() -> new InvalidModFileException("Missing modId entry", owningFile));
this.version = modConfig.<String>getOptional("version").
map(s->StringSubstitutor.replace(s, owningFile != null ? owningFile.getFile() : null )).
map(DefaultArtifactVersion::new).orElse(DEFAULT_VERSION);
this.displayName = modConfig.<String>getOptional("displayName").orElse(null);
this.description = modConfig.get("description");
if (owningFile != null) {
this.dependencies = owningFile.getConfig().<List<UnmodifiableConfig>>getOptional(Arrays.asList("dependencies", this.modId)).
orElse(Collections.emptyList()).stream().map(dep -> new ModVersion(this, dep)).collect(Collectors.toList());
this.properties = owningFile.getConfig().<UnmodifiableConfig>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<ModInfo.ModVersion> 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;
}
}
}

View File

@ -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<ScanResult.AnnotationData> annotations = new ArrayList<>();
private final List<ScanResult.ClassData> classes = new ArrayList<>();
private Map<String,? extends IModLanguageProvider.IModLanguageLoader> modTargets;
public ScanResult(final ModFile file) {
this.file = file;
@ -52,6 +56,17 @@ public class ScanResult {
return annotations;
}
public void addLanguageLoader(Map<String,? extends IModLanguageProvider.IModLanguageLoader> modTargetMap)
{
modTargets = modTargetMap;
}
public Map<String, ? extends IModLanguageProvider.IModLanguageLoader> getTargets()
{
return modTargets;
}
public static class ClassData {
private final Type clazz;
private final Type parent;

View File

@ -45,9 +45,9 @@ public class Scanner {
public ScanResult scan() {
ScanResult result = new ScanResult(fileToScan);
fileToScan.scanFile(p -> fileVisitor(p, result));
final Set<IModLanguageProvider> 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;
}

View File

@ -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<ModelResourceLocation, IBakedModel> modelRegistry;
private static Multimap<ModelResourceLocation, IBlockState> reverseBlockMap = HashMultimap.create();
private static Multimap<ModelResourceLocation, String> reverseItemMap = HashMultimap.create();
private static void buildLookups(final BlockModelShapes blockModelShapes, Function<Item,Iterable<String>> itemNameLookup) {
if (!reverseBlockMap.isEmpty()) return;
for(Map.Entry<IBlockState, ModelResourceLocation> 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<ModelResourceLocation, IBakedModel> modelRegistry, BlockModelShapes blockModelShapes, Function<Item, Iterable<String>> 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<IBlockState> 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<String> 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)
{
}
}

View File

@ -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<V extends IForgeRegistryEntry<V>> 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<V extends IForgeRegistryEntry<V>> implements IForgeRe
{
try
{
ReflectionHelper.findMethod(BitSet.class, "trimToSize", null).invoke(this.availabilityMap);
// ReflectionHelper.findMethod(BitSet.class, "trimToSize", null).invoke(this.availabilityMap);
}
catch (Exception e)
{

View File

@ -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<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
{
final Class<? extends IForgeRegistryEntry> 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" })

View File

@ -1,2 +1,2 @@
net.minecraftforge.fml.loading.FMLLaunchProvider
net.minecraftforge.fml.loading.FMLDevLaunchProvider
net.minecraftforge.fml.loading.FMLDevClientLaunchProvider

View File

@ -0,0 +1,6 @@
modId="forge"
version="${global.forgeVersion}"
displayName="Forge"
description='''
Forge, the magic
'''

View File

@ -0,0 +1,6 @@
modId="minecraft"
version="${global.mcVersion}"
displayName="Minecraft"
description='''
Minecraft, a modded story
'''

View File

@ -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);
}
}

View File

@ -0,0 +1,5 @@
{
"reply": {
"message": "no"
}
}

View File

@ -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"