separated fmllauncher code from main forge mod code. This enables much

stronger classloader separation between the two sides. Forge now
loads as a regular mod.

Still needs a bunch of debugging, but structure looks good and game loads
in forge dev.
This commit is contained in:
cpw committed 2018-12-31 16:34:33 -05:00
1 parent 558e1d2fb0
commit 48846bc0ba
72 files changed
+693 -755

No files matched your search

@@ -1,56 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import static net.minecraftforge.fml.Logging.CORE;
public class FileUtils
{
private static final Logger LOGGER = LogManager.getLogger();
public static Path getOrCreateDirectory(Path dirPath, String dirLabel) {
if (!Files.isDirectory(dirPath))
{
LOGGER.debug(CORE,"Making {} directory : {}", dirLabel, dirPath);
try {
Files.createDirectory(dirPath);
} catch (IOException e) {
if (e instanceof FileAlreadyExistsException) {
LOGGER.error(CORE,"Failed to create {} directory - there is a file in the way", dirLabel);
} else {
LOGGER.error(CORE,"Problem with creating {} directory (Permissions?)", dirLabel, e);
}
throw new RuntimeException("Problem creating directory", e);
}
LOGGER.debug(CORE,"Created {} directory : {}", dirLabel, dirPath);
} else {
LOGGER.debug(CORE,"Found existing {} directory : {}", dirLabel, dirPath);
}
return dirPath;
}
}
@@ -1,43 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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]));
}
}
@@ -68,7 +68,9 @@ public class LaunchTesting
"--version", "FMLDev",
"--assetIndex", "1.13",
"--assetsDir", assets,
"--userProperties", "{}");
"--userProperties", "{}",
"--fml.forgeVersion", "24.0.0",
"--fml.mcVersion", "1.13");
} else if (Objects.equals(target, "fmldevserver")) {
String[] launchargs = ObjectArrays.concat(new String[] {"--launchTarget", target,
"--gameDir", "."}, args, String.class);
@@ -21,6 +21,7 @@ package net.minecraftforge.fml;
import net.minecraftforge.fml.common.event.ModLifecycleEvent;
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
import net.minecraftforge.fml.language.ILifecycleEvent;
import java.util.List;
import java.util.function.Consumer;
@@ -46,7 +47,7 @@ public enum LifecycleEventProvider
}
public static class LifecycleEvent {
public static class LifecycleEvent implements ILifecycleEvent<LifecycleEvent> {
private final ModLoadingStage stage;
public LifecycleEvent(ModLoadingStage stage)
@@ -92,7 +92,7 @@ public class ModList
}
public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> errorHandler) {
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.preLifecycleEvent(lifecycleEvent));
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
DeferredWorkQueue.deferredWorkQueue.clear();
try
{
@@ -105,7 +105,7 @@ public class ModList
LOGGER.debug(LOADING, "Dispatching synchronous work, {} jobs", DeferredWorkQueue.deferredWorkQueue.size());
DeferredWorkQueue.deferredWorkQueue.forEach(FutureTask::run);
LOGGER.debug(LOADING, "Synchronous work queue complete");
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.postLifecycleEvent(lifecycleEvent));
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
}
public void setLoadedMods(final List<ModContainer> modContainers)
@@ -28,6 +28,7 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureClassLoader;
import java.util.function.Predicate;
import static net.minecraftforge.fml.Logging.LOADING;
@@ -39,8 +40,11 @@ public class ModLoadingClassLoader extends SecureClassLoader
ClassLoader.registerAsParallelCapable();
}
private final Predicate<String> classLoadingPredicate;
protected ModLoadingClassLoader(final ClassLoader parent) {
super(parent);
this.classLoadingPredicate = FMLLoader.getClassLoaderExclusions();
}
@Override
@@ -49,21 +53,29 @@ public class ModLoadingClassLoader extends SecureClassLoader
return super.getResource(name);
}
/*
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
final String className = name.replace('.', '/').concat(".class");
final Path classResource = FMLLoader.getLoadingModList().findResource(className);
if (classResource != null)
{
return findClass(name);
}
return super.loadClass(name, resolve);
}
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
if (!classLoadingPredicate.test(name)) {
LOGGER.debug(LOADING, "Delegating to parent {}", name);
return getParent().loadClass(name);
}
LOGGER.debug(LOADING, "Loading class {}", name);
final String className = name.replace('.','/').concat(".class");
final Path classResource = FMLLoader.getLoadingModList().findResource(className);
@@ -1,63 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.common.toposort.ModSortingException;
import net.minecraftforge.fml.common.toposort.ModSortingException.SortingExceptionData;
public class GuiSortingProblem extends GuiScreen {
private SortingExceptionData<ModContainer> failedList;
public GuiSortingProblem(ModSortingException modSorting)
{
this.failedList = modSorting.getExceptionData();
}
@Override
public void initGui()
{
super.initGui();
}
@Override
public void render(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
int offset = Math.max(85 - (failedList.getVisitedNodes().size() + 3) * 10, 10);
this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found a problem with your minecraft installation", this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, "A mod sorting cycle was detected and loading cannot continue", this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, String.format("The first mod in the cycle is %s", failedList.getFirstBadNode()), this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, "The remainder of the cycle involves these mods", this.width / 2, offset, 0xFFFFFF);
offset+=5;
for (ModContainer mc : failedList.getVisitedNodes())
{/* TODO Mod dependencies
offset+=10;
this.drawCenteredString(this.fontRenderer, String.format("%s : before: %s, after: %s", mc.toString(), mc.getDependants(), mc.getDependencies()), this.width / 2, offset, 0xEEEEEE);
*/}
offset+=20;
this.drawCenteredString(this.fontRenderer, "The file 'ForgeModLoader-client-0.log' contains more information", this.width / 2, offset, 0xFFFFFF);
}
}
@@ -1,93 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.common.toposort;
import java.util.Set;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.client.IDisplayableError;
import net.minecraftforge.fml.client.gui.GuiSortingProblem;
import net.minecraftforge.fml.common.EnhancedRuntimeException;
import net.minecraftforge.fml.common.EnhancedRuntimeException.WrappedPrintStream;
public class ModSortingException extends EnhancedRuntimeException implements IDisplayableError
{
private static final long serialVersionUID = 1L;
public class SortingExceptionData<T>
{
public SortingExceptionData(T node, Set<T> visitedNodes)
{
this.firstBadNode = node;
this.visitedNodes = visitedNodes;
}
private T firstBadNode;
private Set<T> visitedNodes;
public T getFirstBadNode()
{
return firstBadNode;
}
public Set<T> getVisitedNodes()
{
return visitedNodes;
}
}
private SortingExceptionData<?> sortingExceptionData;
public <T> ModSortingException(String string, T node, Set<T> visitedNodes)
{
super(string);
this.sortingExceptionData = new SortingExceptionData<>(node, visitedNodes);
}
@SuppressWarnings("unchecked")
public <T> SortingExceptionData<T> getExceptionData()
{
return (SortingExceptionData<T>) sortingExceptionData;
}
@Override
protected void printStackTrace(WrappedPrintStream stream)
{
SortingExceptionData<ModContainer> exceptionData = getExceptionData();
stream.println("A dependency cycle was detected in the input mod set so an ordering cannot be determined");
stream.println("The first mod in the cycle is " + exceptionData.getFirstBadNode());
stream.println("The mod cycle involves:");
for (ModContainer mc : exceptionData.getVisitedNodes())
{/* TODO Mod dependencies
stream.println(String.format("\t%s : before: %s, after: %s", mc.toString(), mc.getDependants(), mc.getDependencies()));
*/}
}
@Override
@OnlyIn(Dist.CLIENT)
public GuiScreen createGui()
{
return new GuiSortingProblem(this);
}
}
@@ -1,275 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.common.toposort;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.StringBuilderFormattable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Topological sort for mod loading
*
* Based on a variety of sources, including http://keithschwarz.com/interesting/code/?dir=topological-sort
* @author cpw
*
*/
public class TopologicalSort
{
public static class DirectedGraph<T> implements Iterable<T>
{
private final Map<T, SortedSet<T>> graph = new HashMap<>();
private List<T> orderedNodes = new ArrayList<>();
public boolean addNode(T node)
{
// Ignore nodes already added
if (graph.containsKey(node))
{
return false;
}
orderedNodes.add(node);
graph.put(node, new TreeSet<>(Comparator.comparingInt(o -> orderedNodes.indexOf(o))));
return true;
}
public void addEdge(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
graph.get(from).add(to);
}
public void removeEdge(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
graph.get(from).remove(to);
}
public boolean edgeExists(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
return graph.get(from).contains(to);
}
public Set<T> edgesFrom(T from)
{
if (!graph.containsKey(from))
{
throw new NoSuchElementException("Missing node from graph");
}
return Collections.unmodifiableSortedSet(graph.get(from));
}
@Override
public Iterator<T> iterator()
{
return orderedNodes.iterator();
}
public int size()
{
return graph.size();
}
public boolean isEmpty()
{
return graph.isEmpty();
}
@Override
public String toString()
{
return graph.toString();
}
}
/**
* Sort the input graph into a topologically sorted list
*
* Uses the reverse depth first search as outlined in ...
* @param graph
* @return The sorted mods list.
*/
public static <T> List<T> topologicalSort(DirectedGraph<T> graph)
{
DirectedGraph<T> rGraph = reverse(graph);
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<>();
for (T node : rGraph)
{
explore(node, rGraph, sortedResult, visitedNodes, expandedNodes);
}
return sortedResult;
}
public static <T> DirectedGraph<T> reverse(DirectedGraph<T> graph)
{
DirectedGraph<T> result = new DirectedGraph<>();
for (T node : graph)
{
result.addNode(node);
}
for (T from : graph)
{
for (T to : graph.edgesFrom(from))
{
result.addEdge(to, from);
}
}
return result;
}
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))
{
// And have completed this node before
if (expandedNodes.contains(node))
{
// Then we're fine
return;
}
throw new TopoSortException(new TopoSortException.TopoSortExceptionData<>(node, sortedResult, visitedNodes, expandedNodes));
}
// Visit this node
visitedNodes.add(node);
// Recursively explore inbound edges
for (T inbound : graph.edgesFrom(node))
{
explore(inbound, graph, sortedResult, visitedNodes, expandedNodes);
}
// Add ourselves now
sortedResult.add(node);
// And mark ourselves as explored
expandedNodes.add(node);
}
public static class TopoSortException extends RuntimeException {
private final TopoSortExceptionData<?> topoSortData;
public static class TopoSortExceptionData<T> implements Message, StringBuilderFormattable
{
private final List<T> sortedResult;
private final Set<T> visitedNodes;
private final Set<T> expandedNodes;
private final T node;
TopoSortExceptionData(T node, List<T> sortedResult, Set<T> visitedNodes, Set<T> expandedNodes)
{
this.node = node;
this.sortedResult = sortedResult;
this.visitedNodes = visitedNodes;
this.expandedNodes = expandedNodes;
}
@Override
public String getFormattedMessage()
{
return "";
}
@Override
public String getFormat()
{
return "";
}
@Override
public Object[] getParameters()
{
return new Object[0];
}
@Override
public Throwable getThrowable()
{
return null;
}
@Override
public void formatTo(StringBuilder buffer)
{
buffer.append("Mod Sorting failed.\n");
buffer.append("Visiting node {}\n").append(String.valueOf(node));
buffer.append("Current sorted list : {}\n").append(String.valueOf(sortedResult));
buffer.append("Visited set for this node : {}\n").append(String.valueOf(visitedNodes));
buffer.append("Explored node set : {}\n").append(expandedNodes);
buffer.append("Likely cycle is in : {}\n").append(Sets.difference(visitedNodes, expandedNodes));
}
public List<EarlyLoadingException.ExceptionData> toExceptionData(Function<T, String> nodeMapper) {
return Collections.singletonList(
new EarlyLoadingException.ExceptionData("fml.messages.cycleproblem",
nodeMapper.apply(node),
visitedNodes.stream().map(nodeMapper).collect(Collectors.joining(","))));
}
}
public <T> TopoSortException(TopoSortExceptionData<T> data)
{
this.topoSortData = data;
}
@SuppressWarnings("unchecked")
public <T> TopoSortExceptionData<T> getData() {
return (TopoSortExceptionData<T>)this.topoSortData;
}
}
}
@@ -19,7 +19,7 @@
package net.minecraftforge.fml.javafmlmod;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.language.ILifecycleEvent;
import net.minecraftforge.fml.language.IModLanguageProvider;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.language.ModFileScanData;
@@ -32,6 +32,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.LOADING;
@@ -100,12 +101,7 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
}
@Override
public void preLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
public <R extends ILifecycleEvent<R>> void consumeLifecycleEvent(final Supplier<R> consumeEvent) {
@Override
public void postLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
}
@@ -1,39 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.language;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import org.apache.maven.artifact.versioning.VersionRange;
import java.net.URL;
import java.util.List;
public interface IModFileInfo
{
List<IModInfo> getMods();
UnmodifiableConfig getConfig();
URL getUpdateURL(IModFileInfo modFileInfo);
String getModLoader();
VersionRange getModLoaderVersion();
}
@@ -1,117 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.language;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import net.minecraftforge.fml.loading.MavenVersionAdapter;
import net.minecraftforge.fml.loading.FMLEnvironment;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import java.util.List;
public interface IModInfo
{
VersionRange UNBOUNDED = MavenVersionAdapter.createFromVersionSpec("");
IModFileInfo getOwningFile();
String getModId();
String getDisplayName();
String getDescription();
ArtifactVersion getVersion();
List<ModVersion> getDependencies();
UnmodifiableConfig getModConfig();
enum Ordering {
BEFORE, AFTER, NONE
}
enum DependencySide {
CLIENT, SERVER, BOTH;
public boolean isCorrectSide()
{
return this == BOTH || FMLEnvironment.dist.name().equals(this.name());
}
}
class ModVersion {
private IModInfo owner;
private final String modId;
private final VersionRange versionRange;
private final boolean mandatory;
private final Ordering ordering;
private final DependencySide side;
public ModVersion(final IModInfo owner, final UnmodifiableConfig config) {
this.owner = owner;
this.modId = config.get("modId");
this.versionRange = config.getOptional("versionRange").map(String.class::cast).
map(MavenVersionAdapter::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 IModInfo owner)
{
this.owner = owner;
}
public IModInfo getOwner()
{
return owner;
}
}
}
@@ -1,44 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.language;
import net.minecraftforge.fml.LifecycleEventProvider;
import java.util.function.Consumer;
/**
* Loaded as a ServiceLoader, from the classpath. ExtensionPoint are loaded from
* the mods directory, with the FMLType META-INF of LANGPROVIDER.
*
* Version data is read from the manifest's implementation version.
*/
public interface IModLanguageProvider
{
String name();
Consumer<ModFileScanData> getFileVisitor();
void preLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent);
void postLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent);
interface IModLanguageLoader {
<T> T loadMod(IModInfo info, ClassLoader modClassLoader, ModFileScanData modFileScanResults);
}
}
@@ -1,102 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.language;
import org.objectweb.asm.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
public class ModFileScanData
{
private final List<ModFileScanData.AnnotationData> annotations = new ArrayList<>();
private final List<ModFileScanData.ClassData> classes = new ArrayList<>();
private Map<String,? extends IModLanguageProvider.IModLanguageLoader> modTargets;
private Map<String,?> functionalScanners;
public static Predicate<Type> interestingAnnotations() {
return t->true;
}
public List<ModFileScanData.ClassData> getClasses() {
return classes;
}
public List<ModFileScanData.AnnotationData> getAnnotations() {
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;
private final Set<Type> interfaces;
public ClassData(final Type clazz, final Type parent, final Set<Type> interfaces) {
this.clazz = clazz;
this.parent = parent;
this.interfaces = interfaces;
}
}
public static class AnnotationData {
private final Type annotationType;
private final Type clazz;
private final String memberName;
private final Map<String,Object> annotationData;
public AnnotationData(final Type annotationType, final Type clazz, final String memberName, final Map<String, Object> annotationData) {
this.annotationType = annotationType;
this.clazz = clazz;
this.memberName = memberName;
this.annotationData = annotationData;
}
public Type getAnnotationType() {
return annotationType;
}
public Type getClassType() {
return clazz;
}
public String getMemberName() {
return memberName;
}
public Map<String, Object> getAnnotationData() {
return annotationData;
}
}
}
@@ -1,69 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.electronwill.nightconfig.core.file.FileConfig;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
public class DefaultModInfos
{
static {
FileConfig minecraftmod; FileConfig forgemod;
try
{
final URI jarFileURI = DefaultModInfos.class.getClassLoader().getResource("minecraftmod.toml").toURI();
if (Objects.equals(jarFileURI.getScheme(), "jar")) {
// Initialize the filesystem for the forge jar, because otherwise this barfs?
FileSystems.newFileSystem(jarFileURI, new HashMap<>());
}
minecraftmod = FileConfig.of(Paths.get(jarFileURI));
minecraftmod.load();
}
catch (IOException | URISyntaxException | NullPointerException e)
{
throw new RuntimeException("Missing toml configs for minecraft and forge!", e);
}
minecraftModInfo = new ModInfo(null, minecraftmod);
}
public static final IModInfo minecraftModInfo;
// no construction
private DefaultModInfos() {}
public static List<IModInfo> getModInfos()
{
return Arrays.asList(minecraftModInfo);
}
}
@@ -1,60 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 java.util.Arrays;
import java.util.List;
/**
* Thrown during early loading phase, and collected by the LoadingModList for handoff to the client
* or server.
*/
public class EarlyLoadingException extends RuntimeException {
public static class ExceptionData {
private final String i18message;
private final Object[] args;
public ExceptionData(final String message, Object... args) {
this.i18message = message;
this.args = args;
}
public String getI18message() {
return i18message;
}
public Object[] getArgs() {
return args;
}
}
private final List<ExceptionData> errorMessages;
public List<ExceptionData> getAllData() {
return errorMessages;
}
EarlyLoadingException(final String message, final Throwable originalException, List<ExceptionData> errorMessages) {
super(message, originalException);
this.errorMessages = errorMessages;
}
}
@@ -1,84 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.relauncher.libraries.LibraryManager;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;
public class FMLClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name()
{
return "fmlclient";
}
@Override
public Path[] identifyTransformationTargets()
{
Path libsPath = findLibsPath();
Path patchedBinariesPath = libsPath.resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion(), "forge-" + MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion() + "-client.jar"));
Path srgMcPath = libsPath.resolve(Paths.get("net","minecraft", "client", MCPVersion.getMCPandMCVersion(), "client-"+MCPVersion.getMCPandMCVersion()+"-srg.jar"));
LOGGER.info("SRG MC at {} is {}", srgMcPath.toString(), Files.exists(srgMcPath) ? "present" : "missing");
LOGGER.info("Forge patches at {} is {}", patchedBinariesPath.toString(), Files.exists(patchedBinariesPath) ? "present" : "missing");
LOGGER.info("Forge at {} is {}", getForgePath().toString(), Files.exists(getForgePath()) ? "present" : "missing");
if (!(Files.exists(srgMcPath) && Files.exists(patchedBinariesPath) && Files.exists(getForgePath()))) {
throw new RuntimeException("Failed to find patched jars");
}
return super.commonLibPaths(new Path[] {getForgePath(), patchedBinariesPath, srgMcPath});
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Class.forName("net.minecraft.client.main.Main", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@Override
public void setup(final IEnvironment environment) {
}
@Override
public Dist getDist()
{
return Dist.CLIENT;
}
}
@@ -1,105 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.ObjectArrays;
import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import static net.minecraftforge.fml.Logging.CORE;
public abstract class FMLCommonLaunchHandler
{
private static final Logger LOGGER = LogManager.getLogger();
private static final List<String> SKIPPACKAGES = Arrays.asList(
// standard libs
"joptsimple.", "org.lwjgl.", "com.mojang.guava.", "com.google.", "org.apache.commons.", "io.netty.",
"org.apache.logging.log4j.", "org.apache.http.", "org.apache.maven.", "org.objectweb.asm.",
"paulscode.sound.", "com.ibm.icu.", "sun.", "gnu.trove.", "com.electronwill.nightconfig.",
"net.minecraftforge.fml.loading.", "net.minecraftforge.fml.language.", "net.minecraftforge.versions.",
"net.minecraftforge.eventbus.", "net.minecraftforge.api."
);
private Path forgePath;
protected Predicate<String> getPackagePredicate() {
return cn -> SKIPPACKAGES.stream().noneMatch(cn::startsWith);
}
public void setup(final IEnvironment environment)
{
}
Path findLibsPath() {
final Path asm = findJarPathFor("org/objectweb/asm/Opcodes.class", "asm");
// go up SIX parents to find the libs dir
final Path libs = asm.getParent().getParent().getParent().getParent().getParent().getParent();
LOGGER.debug(CORE, "Found probable library path {}", libs);
return libs;
}
Path findJarPathFor(final String className, final String jarName) {
final URL resource = getClass().getClassLoader().getResource(className);
try {
Path path;
final URI uri = resource.toURI();
if (uri.getRawSchemeSpecificPart().contains("!")) {
path = Paths.get(new URI(uri.getRawSchemeSpecificPart().split("!")[0]));
} else {
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-className.length())));
}
LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
return path;
} catch (URISyntaxException e) {
LOGGER.error(CORE, "Failed to find JAR for class {} - {}", className, jarName);
throw new RuntimeException("Unable to locate "+className+" - "+jarName, e);
}
}
Path[] commonLibPaths(Path[] extras) {
final Path realms = findJarPathFor("com/mojang/realmsclient/RealmsVersion.class", "realms");
return ObjectArrays.concat(extras, realms);
}
Path getForgePath() {
if (forgePath == null) {
forgePath = findJarPathFor("net/minecraftforge/versions/forge/ForgeVersion.class", "forge");
LOGGER.debug(CORE, "Found forge path {}", forgePath);
}
return forgePath;
}
public abstract Dist getDist();
protected void beforeStart(ITransformingClassLoader launchClassLoader)
{
FMLLoader.beforeStart(launchClassLoader, getForgePath());
}
}
@@ -1,80 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.electronwill.nightconfig.core.ConfigSpec;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.CORE;
public class FMLConfig
{
private static FMLConfig INSTANCE = new FMLConfig();
private static ConfigSpec configSpec = new ConfigSpec();
static {
configSpec.define("splashscreen", Boolean.TRUE);
configSpec.defineInList("side", Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.defineInRange("maxframerate", 60, 10, 120);
configSpec.defineInRange("minframerate", 60, 10, 120);
/* Tests that we know work and shouldn't be done in runtime.
configSpec.defineInList(Arrays.asList("tasty","flavour"), Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.defineInList(Arrays.asList("tasty","teaser"), Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.define("longstring", StringUtils.repeat("AAAA", 10000), s->s!=null && ((String)s).length()>0);
*/
}
private CommentedFileConfig configData;
private void loadFrom(final Path configFile)
{
configData = CommentedFileConfig.builder(configFile).sync().
defaultResource("/META-INF/defaultfmlconfig.toml").
autosave().autoreload().
writingMode(WritingMode.REPLACE).
build();
configData.load();
if (!configSpec.isCorrect(configData)) {
LogManager.getLogger().warn(CORE, "Configuration file {} is not correct. Correcting", configFile);
configSpec.correct(configData, (action, path, incorrectValue, correctedValue) ->
LogManager.getLogger().warn(CORE, "Incorrect key {} was corrected from {} to {}", path, incorrectValue, correctedValue));
}
configData.save();
}
public static void load()
{
final Path configFile = FMLPaths.FMLCONFIG.get();
INSTANCE.loadFrom(configFile);
LogManager.getLogger().debug(CORE, "Loaded FML config from {}", FMLPaths.FMLCONFIG.get());
LogManager.getLogger().debug(CORE, "Splash screen is {}", INSTANCE.splashScreenEnabled());
}
public boolean splashScreenEnabled() {
return configData.<Boolean>getOptional("splashscreen").orElse(Boolean.FALSE);
}
}
@@ -1,79 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import static net.minecraftforge.fml.Logging.CORE;
public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name()
{
return "fmldevclient";
}
@Override
public Path[] identifyTransformationTargets()
{
return super.commonLibPaths(new Path[] {getForgePath()});
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
LOGGER.debug(CORE, "Launching minecraft in {} with arguments {}", launchClassLoader, arguments);
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Class.forName("net.minecraft.client.main.Main", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@Override
public void setup(IEnvironment environment)
{
LOGGER.debug(CORE, "No jar creation necessary. Launch is dev environment");
}
@Override
public Dist getDist()
{
return Dist.CLIENT;
}
}
@@ -1,80 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
import static net.minecraftforge.fml.Logging.CORE;
public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name()
{
return "fmldevserver";
}
@Override
public Path[] identifyTransformationTargets()
{
return super.commonLibPaths(new Path[] { getForgePath() });
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
LOGGER.debug(CORE, "Launching minecraft in {} with arguments {}", launchClassLoader, arguments);
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Thread.currentThread().setContextClassLoader(launchClassLoader.getInstance());
Class.forName("net.minecraft.server.MinecraftServer", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@Override
public void setup(IEnvironment environment)
{
LOGGER.debug(CORE, "No jar creation necessary. Launch is dev environment");
}
@Override
public Dist getDist()
{
return Dist.DEDICATED_SERVER;
}
}
@@ -1,27 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.api.distmarker.Dist;
public class FMLEnvironment
{
public static final Dist dist = FMLLoader.getDist();
}
@@ -1,210 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 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.distmarker.Dist;
import net.minecraftforge.versions.forge.ForgeVersion;
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;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.Logging.SCAN;
public class FMLLoader
{
private static final Logger LOGGER = LogManager.getLogger();
private static ILaunchPluginService accessTransformer;
private static ModDiscoverer modDiscoverer;
private static ICoreModProvider coreModProvider;
private static ILaunchPluginService eventBus;
private static LanguageLoadingProvider languageLoadingProvider;
private static Dist dist;
private static LoadingModList loadingModList;
private static ClassLoader launchClassLoader;
private static RuntimeDistCleaner runtimeDistCleaner;
private static Path gamePath;
private static Path forgePath;
static void onInitialLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException
{
final String version = ForgeVersion.getVersion();
LOGGER.debug(CORE,"FML {} loading", version);
final Package modLauncherPackage = ITransformationService.class.getPackage();
LOGGER.debug(CORE,"FML found ModLauncher version : {}", modLauncherPackage.getImplementationVersion());
if (!modLauncherPackage.isCompatibleWith("1.0")) {
LOGGER.error(CORE,"Found incompatible ModLauncher specification : {}, version {} from {}", modLauncherPackage.getSpecificationVersion(), modLauncherPackage.getImplementationVersion(), modLauncherPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible modlauncher found "+modLauncherPackage.getSpecificationVersion());
}
LOGGER.debug(CORE, "Initializing modjar URL handler");
URL.setURLStreamHandlerFactory(p->p.equals("modjar") ? new ModJarURLHandler() : null);
accessTransformer = environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run"));
final Package atPackage = accessTransformer.getClass().getPackage();
LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion());
if (!atPackage.isCompatibleWith("1.0")) {
LOGGER.error(CORE,"Found incompatible AccessTransformer specification : {}, version {} from {}", atPackage.getSpecificationVersion(), atPackage.getImplementationVersion(), atPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible accesstransformer found "+atPackage.getSpecificationVersion());
}
eventBus = environment.findLaunchPlugin("eventbus").orElseThrow(()-> new IncompatibleEnvironmentException("Missing EventBus, cannot run"));
final Package eventBusPackage = eventBus.getClass().getPackage();
LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion());
if (!eventBusPackage.isCompatibleWith("1.0")) {
LOGGER.error(CORE,"Found incompatible EventBus specification : {}, version {} from {}", eventBusPackage.getSpecificationVersion(), eventBusPackage.getImplementationVersion(), eventBusPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible eventbus found "+eventBusPackage.getSpecificationVersion());
}
runtimeDistCleaner = (RuntimeDistCleaner)environment.findLaunchPlugin("runtimedistcleaner").orElseThrow(()->new IncompatibleEnvironmentException("Missing RuntimeDistCleaner, cannot run!"));
LOGGER.debug(CORE, "Found Runtime Dist Cleaner");
final ArrayList<ICoreModProvider> coreModProviders = new ArrayList<>();
ServiceLoader.load(ICoreModProvider.class).forEach(coreModProviders::add);
if (coreModProviders.isEmpty()) {
LOGGER.error(CORE, "Found no coremod provider. Cannot run");
throw new IncompatibleEnvironmentException("No coremod library found");
} else if (coreModProviders.size() > 1) {
LOGGER.error(CORE, "Found multiple coremod providers : {}. Cannot run", coreModProviders.stream().map(p -> p.getClass().getName()).collect(Collectors.toList()));
throw new IncompatibleEnvironmentException("Multiple coremod libraries found");
}
coreModProvider = coreModProviders.get(0);
final Package coremodPackage = coreModProvider.getClass().getPackage();
LOGGER.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion());
languageLoadingProvider = new LanguageLoadingProvider();
}
static void setupLaunchHandler(final IEnvironment environment)
{
final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING");
final Optional<ILaunchHandlerService> launchHandler = environment.findLaunchHandler(launchTarget);
LOGGER.debug(CORE, "Using {} as launch service", launchTarget);
if (!launchHandler.isPresent()) {
LOGGER.error(CORE,"Missing LaunchHandler {}, cannot continue", launchTarget);
throw new RuntimeException("Missing launch handler");
}
if (!(launchHandler.get() instanceof FMLCommonLaunchHandler)) {
LOGGER.error(CORE, "Incompatible Launch handler found - type {}, cannot continue", launchHandler.get().getClass().getName());
throw new RuntimeException("Incompatible launch handler found");
}
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".").toAbsolutePath());
FMLCommonLaunchHandler commonLaunchHandler = (FMLCommonLaunchHandler)launchHandler.get();
commonLaunchHandler.setup(environment);
dist = commonLaunchHandler.getDist();
runtimeDistCleaner.getExtension().accept(dist);
}
public static void beginModScan()
{
LOGGER.debug(SCAN,"Scanning for Mod Locators");
modDiscoverer = new ModDiscoverer();
final BackgroundScanHandler backgroundScanHandler = modDiscoverer.discoverMods();
loadingModList = backgroundScanHandler.getLoadingModList();
}
public static ICoreModProvider getCoreModProvider() {
return coreModProvider;
}
public static LanguageLoadingProvider getLanguageLoadingProvider()
{
return languageLoadingProvider;
}
public static void loadAccessTransformer()
{
final URL resource = FMLLoader.class.getClassLoader().getResource("forge_at.cfg");
if (resource == null) {
throw new RuntimeException("Missing forge_at.cfg file");
}
try
{
LOGGER.debug(CORE, "Loading forge_at.cfg into access transformer");
accessTransformer.addResource(Paths.get(resource.toURI()), "forge_at.cfg");
}
catch (URISyntaxException e)
{
LOGGER.error("Error loading forge_at.cfg file", e);
throw new RuntimeException(e);
}
}
public static void addAccessTransformer(Path atPath, ModFile modName)
{
LOGGER.debug(SCAN, "Adding Access Transformer in {}", modName.getFilePath());
accessTransformer.addResource(atPath, modName.getFileName());
}
public static Dist getDist()
{
return dist;
}
public static void beforeStart(ITransformingClassLoader launchClassLoader, Path forgePath)
{
FMLLoader.launchClassLoader = launchClassLoader.getInstance();
FMLLoader.forgePath = forgePath;
}
public static LoadingModList getLoadingModList()
{
return loadingModList;
}
public static ClassLoader getLaunchClassLoader()
{
return launchClassLoader;
}
public static Path getGamePath()
{
return gamePath;
}
public static Path getForgePath() {
return forgePath;
}
}
@@ -1,88 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import net.minecraftforge.fml.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import static net.minecraftforge.fml.Logging.CORE;
public enum FMLPaths
{
GAMEDIR(),
MODSDIR("mods"),
CONFIGDIR("config"),
FMLCONFIG(false, CONFIGDIR, "fml.toml");
private static final Logger LOGGER = LogManager.getLogger();
private final Path relativePath;
private final boolean isDirectory;
private Path absolutePath;
FMLPaths() {
this("");
}
FMLPaths(String... path) {
relativePath = computePath(path);
this.isDirectory = true;
}
private Path computePath(String... path)
{
return Paths.get(path[0], Arrays.copyOfRange(path, 1, path.length));
}
FMLPaths(boolean isDir, FMLPaths parent, String... path) {
this.relativePath = parent.relativePath.resolve(computePath(path));
this.isDirectory = isDir;
}
public static void setup(IEnvironment env) {
final Path rootPath = env.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElseThrow(() -> new RuntimeException("No game path found"));
loadAbsolutePaths(rootPath);
}
public static void loadAbsolutePaths(Path rootPath)
{
for (FMLPaths path : FMLPaths.values())
{
path.absolutePath = rootPath.resolve(path.relativePath).toAbsolutePath().normalize();
if (path.isDirectory)
{
FileUtils.getOrCreateDirectory(path.absolutePath, path.name());
}
LOGGER.debug(CORE,"Path {} is {}", ()-> path, ()-> path.absolutePath);
}
}
public Path get() {
return absolutePath;
}
}
@@ -1,82 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;
public class FMLServerLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name()
{
return "fmlserver";
}
@Override
public Path[] identifyTransformationTargets()
{
Path libsPath = findLibsPath();
Path patchedBinariesPath = libsPath.resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion(), "forge-" + MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion() + "-server.jar"));
Path srgMcPath = libsPath.resolve(Paths.get("net","minecraft", "server", MCPVersion.getMCPandMCVersion(), "server-"+MCPVersion.getMCPandMCVersion()+"-srg.jar"));
LOGGER.info("SRG MC at {} is {}", srgMcPath.toString(), Files.exists(srgMcPath) ? "present" : "missing");
LOGGER.info("Forge patches at {} is {}", patchedBinariesPath.toString(), Files.exists(patchedBinariesPath) ? "present" : "missing");
LOGGER.info("Forge at {} is {}", getForgePath().toString(), Files.exists(getForgePath()) ? "present" : "missing");
if (!(Files.exists(srgMcPath) && Files.exists(patchedBinariesPath) && Files.exists(getForgePath()))) {
throw new RuntimeException("Failed to find patched jars");
}
return new Path[] {getForgePath(), patchedBinariesPath, srgMcPath};
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Class.forName("net.minecraft.server.MinecraftServer", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@Override
public void setup(final IEnvironment environment) {
}
@Override
public Dist getDist()
{
return Dist.DEDICATED_SERVER;
}
}
@@ -1,97 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ITransformationService;
import cpw.mods.modlauncher.api.ITransformer;
import cpw.mods.modlauncher.api.IncompatibleEnvironmentException;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionSpecBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import static net.minecraftforge.fml.Logging.CORE;
public class FMLServiceProvider implements ITransformationService
{
private static final Logger LOGGER = LogManager.getLogger();
private ArgumentAcceptingOptionSpec<String> modsOption;
private ArgumentAcceptingOptionSpec<String> modListsOption;
private List<String> modsArgumentList;
private List<String> modListsArgumentList;
@Override
public String name()
{
return "fml";
}
@Override
public void initialize(IEnvironment environment)
{
LOGGER.debug(CORE,"Setting up basic FML game directories");
FMLPaths.setup(environment);
LOGGER.debug(CORE,"Loading configuration");
FMLConfig.load();
LOGGER.debug(CORE, "Preparing launch handler");
FMLLoader.setupLaunchHandler(environment);
LOGGER.debug(CORE,"Initiating mod scan");
FMLLoader.beginModScan();
LOGGER.debug(CORE, "Loading access transformers");
FMLLoader.loadAccessTransformer();
}
@Override
public void onLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException
{
FMLLoader.onInitialLoad(environment, otherServices);
}
@Override
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder)
{
modsOption = argumentBuilder.apply("mods", "List of mods to add").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
modListsOption = argumentBuilder.apply("modLists", "JSON modlists").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
}
@Override
public void argumentValues(OptionResult option)
{
modsArgumentList = option.values(modsOption);
modListsArgumentList = option.values(modListsOption);
}
@Nonnull
@Override
public List<ITransformer> transformers()
{
LOGGER.debug(CORE, "Loading coremod transformers");
return new ArrayList<>(FMLLoader.getCoreModProvider().getCoreModTransformers());
}
}
@@ -1,107 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.versions.forge.ForgeVersion;
import net.minecraftforge.fml.language.IModLanguageProvider;
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.CORE;
public class LanguageLoadingProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private final List<IModLanguageProvider> languageProviders = new ArrayList<>();
private final ServiceLoader<IModLanguageProvider> serviceLoader;
private final Map<String, ModLanguageWrapper> languageProviderMap = new HashMap<>();
public void forEach(final Consumer<IModLanguageProvider> consumer)
{
languageProviders.forEach(consumer);
}
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();
String implementationVersion = pkg.getImplementationVersion();
if (implementationVersion == null) {
implementationVersion = ForgeVersion.getSpec();
}
LOGGER.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), implementationVersion);
languageProviderMap.put(lp.name(), new ModLanguageWrapper(lp, new DefaultArtifactVersion(implementationVersion)));
});
}
public void addAdditionalLanguages(List<ModFile> modFiles)
{
if (modFiles==null) return;
Stream<Path> langPaths = modFiles.stream().map(ModFile::getFilePath);
serviceLoader.reload();
}
public IModLanguageProvider findLanguage(ModFile mf, String modLoader, VersionRange modLoaderVersion) {
final String languageFileName = mf.getLocator() instanceof ExplodedDirectoryLocator ? "in-development" : mf.getFileName();
final ModLanguageWrapper mlw = languageProviderMap.get(modLoader);
if (mlw == null) {
LOGGER.error("Missing language {} version {} wanted by {}", modLoader, modLoaderVersion, languageFileName);
throw new EarlyLoadingException("Missing language "+modLoader, null, Collections.singletonList(new EarlyLoadingException.ExceptionData("fml.language.missingversion", modLoader, modLoaderVersion, languageFileName, "null")));
}
if (!modLoaderVersion.containsVersion(mlw.getVersion())) {
LOGGER.error("Missing language {} version {} wanted by {}, found {}", modLoader, modLoaderVersion, languageFileName, mlw.getVersion());
throw new EarlyLoadingException("Missing language "+ modLoader + " matching range "+modLoaderVersion + " found "+mlw.getVersion(), null, Collections.singletonList(new EarlyLoadingException.ExceptionData("fml.language.missingversion", modLoader, modLoaderVersion, languageFileName, mlw.getVersion())));
}
return mlw.getModLanguageProvider();
}
}
@@ -1,122 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.Streams;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Master list of all mods <em>in the loading context. This class cannot refer outside the
* loading package</em>
*/
public class LoadingModList
{
private static LoadingModList INSTANCE;
private final List<ModFileInfo> modFiles;
private final List<ModInfo> sortedList;
private final Map<String, ModFileInfo> fileById;
private BackgroundScanHandler scanner;
private final List<EarlyLoadingException> preLoadErrors;
private LoadingModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
{
this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).map(ModFileInfo.class::cast).collect(Collectors.toList());
this.sortedList = Streams.concat(DefaultModInfos.getModInfos().stream(), sortedList.stream()).
map(ModInfo.class::cast).
collect(Collectors.toList());
this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream).
map(ModInfo.class::cast).
collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile));
this.preLoadErrors = new ArrayList<>();
}
public static LoadingModList of(List<ModFile> modFiles, List<ModInfo> sortedList, final EarlyLoadingException earlyLoadingException)
{
INSTANCE = new LoadingModList(modFiles, sortedList);
if (earlyLoadingException != null)
{
INSTANCE.preLoadErrors.add(earlyLoadingException);
}
return INSTANCE;
}
public static LoadingModList get() {
return INSTANCE;
}
public void addCoreMods()
{
modFiles.stream().map(ModFileInfo::getFile).map(ModFile::getCoreMods).flatMap(List::stream).forEach(FMLLoader.getCoreModProvider()::addCoreMod);
}
public void addAccessTransformers()
{
modFiles.stream().map(ModFileInfo::getFile).forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod)));
}
public void addForScanning(BackgroundScanHandler backgroundScanHandler)
{
this.scanner = backgroundScanHandler;
backgroundScanHandler.setLoadingModList(this);
modFiles.stream().map(ModFileInfo::getFile).forEach(backgroundScanHandler::submitForScanning);
}
public List<ModFileInfo> getModFiles()
{
return modFiles;
}
public Path findResource(final String className)
{
for (ModFileInfo mf : modFiles) {
final Path resource = mf.getFile().findResource(className);
if (Files.exists(resource)) return resource;
}
return null;
}
public ModFileInfo getModFileById(String modid)
{
return this.fileById.get(modid);
}
public List<ModInfo> getMods()
{
return this.sortedList;
}
public List<EarlyLoadingException> getErrors() {
return preLoadErrors;
}
}
@@ -1,42 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import static net.minecraftforge.fml.Logging.CORE;
public final class MavenVersionAdapter {
private static final Logger LOGGER = LogManager.getLogger();
private MavenVersionAdapter() {}
public static VersionRange createFromVersionSpec(final String spec) {
try {
return VersionRange.createFromVersionSpec(spec);
} catch (InvalidVersionSpecificationException e) {
LOGGER.error(CORE, "Failed to parse version spec {}", spec, e);
throw new RuntimeException("Failed to parse spec", e);
}
}
}
@@ -1,54 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.file.Files;
public class ModJarURLHandler extends URLStreamHandler
{
// modjar:///modid!path/to/file
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new URLConnection(url) {
private String modpath;
private String modid;
@Override
public void connect() throws IOException
{
final String path = url.getPath();
final String[] parts = path.split("!");
if (parts.length!=2) throw new IOException("Illegal URL format "+path);
modid = parts[0];
modpath = parts[1];
}
@Override
public InputStream getInputStream() throws IOException
{
return Files.newInputStream(FMLLoader.getLoadingModList().getModFileById(modid).getFile().findResource(modpath));
}
};
}
}
@@ -1,154 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.Java9BackportUtils;
import net.minecraftforge.fml.common.toposort.TopologicalSort;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.LOADING;
public class ModSorter
{
private static final Logger LOGGER = LogManager.getLogger();
private List<ModFile> modFiles;
private List<ModInfo> sortedList;
private Map<String, ModInfo> modIdNameLookup;
private ModSorter(final List<ModFile> modFiles)
{
this.modFiles = modFiles;
}
public static LoadingModList sort(List<ModFile> mods)
{
final ModSorter ms = new ModSorter(mods);
EarlyLoadingException earlyLoadingException = null;
try {
ms.findLanguages();
ms.buildUniqueList();
ms.verifyDependencyVersions();
ms.sort();
} catch (EarlyLoadingException ele) {
earlyLoadingException = ele;
ms.sortedList = Collections.emptyList();
}
return LoadingModList.of(ms.modFiles, ms.sortedList, earlyLoadingException);
}
private void findLanguages() {
modFiles.stream().forEach(mf->mf.identifyLanguage());
}
private void sort()
{
final TopologicalSort.DirectedGraph<Supplier<ModFileInfo>> topoGraph = new TopologicalSort.DirectedGraph<>();
modFiles.stream().map(ModFile::getModFileInfo).map(ModFileInfo.class::cast).forEach(mi -> topoGraph.addNode(() -> mi));
modFiles.stream().map(ModFile::getModInfos).flatMap(Collection::stream).
map(IModInfo::getDependencies).flatMap(Collection::stream).
forEach(dep -> addDependency(topoGraph, dep));
final List<Supplier<ModFileInfo>> sorted;
try
{
sorted = TopologicalSort.topologicalSort(topoGraph);
}
catch (TopologicalSort.TopoSortException e)
{
TopologicalSort.TopoSortException.TopoSortExceptionData<Supplier<ModInfo>> data = e.getData();
LOGGER.error(LOADING, ()-> data);
throw new EarlyLoadingException("Sorting error", e, data.toExceptionData(mi-> mi.get().getModId()));
}
this.sortedList = sorted.stream().map(Supplier::get).map(ModFileInfo::getMods).
flatMap(Collection::stream).map(ModInfo.class::cast).collect(Collectors.toList());
this.modFiles = sorted.stream().map(Supplier::get).map(ModFileInfo::getFile).collect(Collectors.toList());
}
private void addDependency(TopologicalSort.DirectedGraph<Supplier<ModFileInfo>> topoGraph,IModInfo.ModVersion dep)
{
switch (dep.getOrdering()) {
case BEFORE:
topoGraph.addEdge(()->(ModFileInfo)dep.getOwner().getOwningFile(), ()->modIdNameLookup.get(dep.getModId()).getOwningFile());
break;
case AFTER:
topoGraph.addEdge(()->modIdNameLookup.get(dep.getModId()).getOwningFile(), ()->(ModFileInfo)dep.getOwner().getOwningFile());
break;
case NONE:
break;
}
}
private void buildUniqueList()
{
final Stream<ModInfo> modInfos = modFiles.stream().map(ModFile::getModInfos).flatMap(Collection::stream).map(ModInfo.class::cast);
final Map<String, List<ModInfo>> modIds = modInfos.collect(Collectors.groupingBy(IModInfo::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()) {
final List<EarlyLoadingException.ExceptionData> duplicateModErrors = dupedMods.stream().
map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedmod", dm.getValue().get(0))).
collect(Collectors.toList());
throw new EarlyLoadingException("Duplicate mods found", null, duplicateModErrors);
}
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), DefaultModInfos.getModInfos().stream()).collect(Collectors.toMap(IModInfo::getModId, IModInfo::getVersion));
final Map<IModInfo, List<IModInfo.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<IModInfo.ModVersion> mandatoryModVersions = modVersionDependencies.values().stream().flatMap(Collection::stream).
filter(mv -> mv.isMandatory() && mv.getSide().isCorrectSide()).collect(Collectors.toSet());
LOGGER.debug(LOADING, "Found {} mandatory requirements", mandatoryModVersions.size());
final Set<IModInfo.ModVersion> missingVersions = mandatoryModVersions.stream().filter(mv->this.modVersionMatches(mv, modVersions)).collect(Collectors.toSet());
LOGGER.debug(LOADING, "Found {} mandatory mod requirements missing", missingVersions.size());
if (!missingVersions.isEmpty()) {
final List<EarlyLoadingException.ExceptionData> exceptionData = missingVersions.stream().map(mv ->
new EarlyLoadingException.ExceptionData("fml.modloading.missingdependency", mv.getModId(),
mv.getOwner().getModId(), mv.getVersionRange(), modVersions.getOrDefault(mv.getModId(), new DefaultArtifactVersion("null")))).
collect(Collectors.toList());
throw new EarlyLoadingException("Missing mods", null, exceptionData);
}
}
private boolean modVersionMatches(final IModInfo.ModVersion mv, final Map<String, ArtifactVersion> modVersions)
{
return !modVersions.containsKey(mv.getModId()) || !mv.getVersionRange().containsVersion(modVersions.get(mv.getModId()));
}
}
@@ -1,174 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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 java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import com.google.common.collect.Streams;
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
public class RuntimeDistCleaner implements ILaunchPluginService
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker DISTXFORM = MarkerManager.getMarker("DISTXFORM");
private static String DIST;
private static final String ONLYIN = Type.getDescriptor(OnlyIn.class);
@Override
public String name()
{
return "runtimedistcleaner";
}
@Override
public void addResource(Path resource, String name)
{
}
@Override
public ClassNode processClass(ClassNode classNode, Type classType)
{
if (remove(classNode.visibleAnnotations, DIST))
{
LOGGER.debug(DISTXFORM, "Attempted to load class {} for invalid dist {}", classNode.name, DIST);
throw new RuntimeException("Attempted to load class "+ classNode.name + " for invalid dist "+ DIST);
}
Iterator<FieldNode> fields = classNode.fields.iterator();
while(fields.hasNext())
{
FieldNode field = fields.next();
if (remove(field.visibleAnnotations, DIST))
{
LOGGER.debug(DISTXFORM,"Removing field: {}.{}", classNode.name, field.name);
fields.remove();
}
}
LambdaGatherer lambdaGatherer = new LambdaGatherer();
Iterator<MethodNode> methods = classNode.methods.iterator();
while(methods.hasNext())
{
MethodNode method = methods.next();
if (remove(method.visibleAnnotations, DIST))
{
LOGGER.debug(DISTXFORM,"Removing method: {}.{}{}", classNode.name, method.name, method.desc);
methods.remove();
lambdaGatherer.accept(method);
}
}
// remove dynamic lambda methods that are inside of removed methods
List<Handle> dynamicLambdaHandles = lambdaGatherer.getDynamicLambdaHandles();
if (!dynamicLambdaHandles.isEmpty())
{
methods = classNode.methods.iterator();
while (methods.hasNext())
{
MethodNode method = methods.next();
for (Handle dynamicLambdaHandle : dynamicLambdaHandles)
{
if (method.name.equals(dynamicLambdaHandle.getName()) && method.desc.equals(dynamicLambdaHandle.getDesc()))
{
LOGGER.debug(DISTXFORM,"Removing lambda method: {}.{}{}", classNode.name, method.name, method.desc);
methods.remove();
}
}
}
}
return classNode;
}
private boolean remove(final List<AnnotationNode> anns, final String side)
{
return !(anns == null) && anns.stream().
filter(ann->Objects.equals(ann.desc, ONLYIN)).
anyMatch(ann -> !Objects.equals(((String[])ann.values.get(ann.values.indexOf("value")+1))[1], side));
}
@SuppressWarnings("unchecked")
@Override
public Consumer<Dist> getExtension()
{
return (s)-> {
DIST = s.name();
LOGGER.debug(DISTXFORM, "Configuring for Dist {}", DIST);
};
}
@Override
public boolean handlesClass(Type classType, boolean isEmpty)
{
return !isEmpty;
}
private static class LambdaGatherer extends MethodVisitor {
private static final Handle META_FACTORY = new Handle(Opcodes.H_INVOKESTATIC,
"java/lang/invoke/LambdaMetafactory", "metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
false);
private final List<Handle> dynamicLambdaHandles = new ArrayList<>();
public LambdaGatherer() {
super(Opcodes.ASM5);
}
public void accept(MethodNode method) {
Streams.stream(method.instructions.iterator()).
filter(insnNode->insnNode.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN).
forEach(insnNode->insnNode.accept(this));
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs)
{
if (META_FACTORY.equals(bsm))
{
Handle dynamicLambdaHandle = (Handle) bsmArgs[1];
dynamicLambdaHandles.add(dynamicLambdaHandle);
}
}
public List<Handle> getDynamicLambdaHandles()
{
return dynamicLambdaHandles;
}
}
}
@@ -1,67 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.ImmutableMap;
import net.minecraftforge.versions.forge.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("."));
}
}
@@ -1,62 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.commons.lang3.text.StrSubstitutor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
/**
* Created by cpw on 05/06/17.
*/
public class StringUtils
{
public static String toLowerCase(final String str) {
return str.toLowerCase(Locale.ROOT);
}
public static String toUpperCase(final String str) {
return str.toUpperCase(Locale.ROOT);
}
public static boolean endsWith(final String search, final String... endings) {
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);
}
}
@@ -1,100 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.fml.language.ModFileScanData;
import net.minecraftforge.fml.loading.LoadingModList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static net.minecraftforge.fml.Logging.SCAN;
public class BackgroundScanHandler
{
private static final Logger LOGGER = LogManager.getLogger();
private final ExecutorService modContentScanner;
private final List<ModFile> pendingFiles;
private final List<ModFile> scannedFiles;
private final List<ModFile> allFiles;
private LoadingModList loadingModList;
public BackgroundScanHandler() {
modContentScanner = Executors.newSingleThreadExecutor(r -> {
final Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setDaemon(true);
return thread;
});
scannedFiles = new ArrayList<>();
pendingFiles = new ArrayList<>();
allFiles = new ArrayList<>();
}
public void submitForScanning(final ModFile file) {
if (modContentScanner.isShutdown()) {
throw new IllegalStateException("Scanner has shutdown");
}
allFiles.add(file);
pendingFiles.add(file);
final CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
.whenComplete(file::setScanResult)
.whenComplete((r,t)-> this.addCompletedFile(file,r,t));
file.setFutureScanResult(future);
}
private void addCompletedFile(final ModFile file, final ModFileScanData modFileScanData, final Throwable throwable) {
if (throwable != null) {
LOGGER.error(SCAN,"An error occurred scanning file {}", file, throwable);
}
pendingFiles.remove(file);
scannedFiles.add(file);
}
public List<ModFile> getScannedFiles() {
if (!pendingFiles.isEmpty()) {
modContentScanner.shutdown();
try {
modContentScanner.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
return scannedFiles;
}
public List<ModFile> getAllFiles() {
return allFiles;
}
public void setLoadingModList(LoadingModList loadingModList)
{
this.loadingModList = loadingModList;
}
public LoadingModList getLoadingModList()
{
return loadingModList;
}
}
@@ -1,49 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.forgespi.ICoreModFile;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
public class CoreModFile implements ICoreModFile {
private final Path internalPath;
private final ModFile file;
private final String name;
CoreModFile(final String name, final Path path, final ModFile file) {
this.name = name;
this.internalPath = path;
this.file = file;
}
@Override
public Reader readCoreMod() throws IOException {
return Files.newBufferedReader(this.internalPath);
}
@Override
public Path getPath() {
return this.internalPath;
}
}
@@ -1,101 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.Logging.SCAN;
public class ExplodedDirectoryLocator implements IModLocator {
private static final String DIR = System.getProperty("fml.explodedDir", "modclasses");
private static final Logger LOGGER = LogManager.getLogger();
private final Path rootDir;
public ExplodedDirectoryLocator() {
this.rootDir = FileSystems.getDefault().getPath(DIR);
if (!Files.exists(this.rootDir)) {
LOGGER.debug(LOADING,"Creating directory {}" + this.rootDir);
try
{
Files.createDirectory(this.rootDir);
}
catch (IOException e)
{
LOGGER.error(LOADING,"Error creating {}", this.rootDir, e);
throw new RuntimeException(e);
}
}
}
@Override
public List<ModFile> scanMods() {
return Collections.singletonList(new ModFile(rootDir, this));
}
@Override
public String name() {
return "exploded directory";
}
@Override
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
return rootDir.resolve(FileSystems.getDefault().getPath(path[0], Arrays.copyOfRange(path, 1, path.length)));
}
@Override
public void scanFile(final ModFile modFile, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scanning directory {}", rootDir);
try (Stream<Path> files = Files.find(rootDir, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
LOGGER.debug(SCAN,"Directory scan complete {}", rootDir);
}
@Override
public String toString()
{
return "{ExplodedDir locator at "+this.rootDir+"}";
}
@Override
public Optional<Manifest> findManifest(Path file)
{
return Optional.empty();
}
}
@@ -1,244 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.minecraftforge.fml.loading.moddiscovery.IModLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.SCAN;
/*
* Locate Forge in the classpath, this works similar to ClasspathLocator.
* Forge/Minecraft are in the following formats:
* Runtime:
* %lib%/net/minecraftforge/forge/{ver}/forge-{ver}.jar or ./forge-{ver}-universal.jar - Client is in libs, Server is in root
* %lib%/net/minecraftforge/forge/{ver}/forge-{ver}-{client|server}.jar - Forge, Vanilla classes, Patched
* %lib%/net/minecraft/{client|server}/{mcver}/{client|server}-{mcver}-srg.jar - Vanilla classes, SRG, Unpatched
*
* UserDev:
* Forge and vanilla is in a single jar file, patched, on the classpath.
*
* Eclipse:
* bin/{sourceset}/
*
* Intellij: TODO: Verify, I don't use intellij
* out/production/resources/../classes/ - Resources are a separate folder so we have to navigate to classes
*
* Gradle:
* build/classes/{language}/{sourceset}
* build/resources/{sourceset}
*
*/
public class ForgeLocator implements IModLocator
{
private static final Logger LOGGER = LogManager.getLogger();
private static final String FORGE_META = "forgemod.toml";
private static final String MAIN_SOURCESET = "main";
private static final String LIB_RESOURCE = "org/objectweb/asm/Opcodes.class";
private static final String LIB_PATH = "org/ow2/asm/asm".replace('/', File.separatorChar);
private List<Path> roots = new ArrayList<>();
private boolean filterClasses = false;
public ForgeLocator() { }
@Override
public List<ModFile> scanMods()
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(FORGE_META);
if (url == null)
throw new IllegalStateException("Could not locate \"" + FORGE_META + "\", Forge is correupt/not on classpath!");
File base = getRoot(url, FORGE_META);
if (base == null)
throw new IllegalStateException("Could not determine root for Forge resource: " + url);
url = loader.getResource(LIB_RESOURCE);
if (url == null)
throw new IllegalStateException("Could not locate \"" + LIB_RESOURCE + "\" to locate libraries directory.");
File libs = getRoot(url, LIB_RESOURCE);
//If In maven style the libraries folder we need to drop the file name, and version folder
if (libs != null && libs.getParentFile().getParentFile().getAbsolutePath().endsWith(LIB_PATH))
{
int count = 2 + LIB_PATH.split(Pattern.quote("" + File.separatorChar)).length;
for (int x = 0; x < count; x++)
libs = libs.getParentFile();
}
else
{
libs = null;
}
LOGGER.info("Forge Locator: ");
LOGGER.info(" Forge: " + base);
LOGGER.info(" Libraries: " + libs);
Path path = base.toPath();
if (base.isDirectory())
{ // Development workspace.
int count = path.getNameCount();
//Eclipse: /bin/{sourceset}/
if (count >= 2 && MAIN_SOURCESET.equals(path.getName(count - 1).toString()) && "bin".equals(path.getName(count - 2).toString()))
{
roots.add(path);
roots.add(path.getParent().resolve("userdev")); //Do I need this?
} //Intellij: /out/production/resources/ & /out/production/classes/
else if (count >= 1 && "resources".equals(path.getName(count - 1).toString()))
{
roots.add(path.getParent().resolve("classes"));
roots.add(path);
} //Gradle: build/resources/{sourceset} & build/classes/{language}/{sourceset}
else if (count >= 2 && MAIN_SOURCESET.equals(path.getName(count - 1).toString()) && "resources".equals(path.getName(count - 2).toString()))
{
Path classes = path.getParent().getParent().resolve("classes");
roots.add(classes.resolve(MAIN_SOURCESET));
roots.add(classes.resolve("userdev"));
roots.add(path);
roots.add(path.getParent().resolve("userdev"));
}
filterClasses = true;
}
else if (base.isFile())
{
if (libs == null) //User dev workspace.
{
roots.add(path);
}
else //Standard install
{
String dist = FMLEnvironment.dist == Dist.CLIENT ? "client" : "server";
String version = MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion();
roots.add(path); // universal
roots.add(libs.toPath().resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", version, "forge-" + version + "-" + dist + ".jar"))); // Patched classes
roots.add(libs.toPath().resolve(Paths.get("net", "minecraft", dist, MCPVersion.getMCPandMCVersion(), dist + "-" + MCPVersion.getMCPandMCVersion() + "-srg.jar"))); //Vanilla jar in srg names
}
}
else
throw new IllegalStateException("Forge path is neither File or Directory... " + base);
if (roots.isEmpty())
return Collections.emptyList();
roots.forEach(r -> LOGGER.info(" Root: " + r));
return Collections.singletonList(new ModFile(roots.get(0), this));
}
protected File getRoot(URL url, String resource)
{
String path = url.getPath();
if ("jar".equals(url.getProtocol()))
{
int idx = path.indexOf("!/");
path = path.substring(0, idx);
}
else
{
//Strip resource path off the end
path = path.substring(0, path.length() - resource.length());
}
if (path.startsWith("file:"))
path = path.substring(5);
return new File(path).getAbsoluteFile();
}
@Override
public String name()
{
return "forge locator";
}
@Override
public Path findPath(final ModFile modFile, String... path)
{
if (path.length < 1)
throw new IllegalArgumentException("Missing path");
if (path.length == 2 && "META-INF".equals(path[0]) && "mods.toml".equals(path[1]))
path = FORGE_META.split("/");
if (filterClasses && path[path.length - 1].endsWith(".class"))
path[path.length - 1] = path[path.length - 1] + "THIS_FILE_WILL_NEVER_EXITS_DIRTY_HAX";
String[] tail = Arrays.copyOfRange(path, 1, path.length);
for (Path root : roots)
{
Path target = root.resolve(root.getFileSystem().getPath(path[0], tail));
if (Files.exists(target))
return target;
}
return modFile.getFilePath().resolve(modFile.getFilePath().getFileSystem().getPath(path[0], tail));
}
@Override
public void scanFile(final ModFile modFile, final Consumer<Path> pathConsumer)
{
final Set<String> visited = new HashSet<>();
roots.stream().forEach(root ->
{
LOGGER.debug(SCAN,"Scanning Forge: " + root);
try (Stream<Path> files = Files.find(root, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class")))
{
files.forEach(f -> {
String relative = root.relativize(f).toString(); //TODO: Test jared files...
if (visited.add(relative))
pathConsumer.accept(Paths.get(relative));
});
}
catch (IOException e)
{
e.printStackTrace();
}
});
LOGGER.debug(SCAN,"Forge scan complete");
}
@Override
public String toString()
{
return "{Forge locator}";
}
@Override
public Optional<Manifest> findManifest(Path file)
{
return Optional.empty();
}
}
@@ -1,42 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.Manifest;
/**
* Loaded as a ServiceLoader. Takes mechanisms for locating candidate "mods"
* and transforms them into {@link ModFile} objects.
*/
public interface IModLocator {
List<ModFile> scanMods();
String name();
Path findPath(ModFile modFile, String... path);
void scanFile(final ModFile modFile, Consumer<Path> pathConsumer);
Optional<Manifest> findManifest(Path file);
}
@@ -1,33 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.fml.language.IModFileInfo;
public class InvalidModFileException extends RuntimeException
{
private final IModFileInfo modFileInfo;
public InvalidModFileException(String message, IModFileInfo modFileInfo)
{
super(message);
this.modFileInfo = modFileInfo;
}
}
@@ -1,139 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.Map;
import net.minecraftforge.fml.language.ModFileScanData;
import org.objectweb.asm.Type;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class ModAnnotation
{
public static ModFileScanData.AnnotationData fromModAnnotation(final Type clazz, final ModAnnotation annotation) {
return new ModFileScanData.AnnotationData(annotation.asmType, clazz, annotation.member, annotation.values);
}
public static class EnumHolder
{
private final String desc;
private final String value;
public EnumHolder(String desc, String value)
{
this.desc = desc;
this.value = value;
}
public String getDesc()
{
return desc;
}
public String getValue()
{
return value;
}
}
ElementType type;
Type asmType;
String member;
Map<String,Object> values = Maps.newHashMap();
private ArrayList<Object> arrayList;
private String arrayName;
public ModAnnotation(ElementType type, Type asmType, String member)
{
this.type = type;
this.asmType = asmType;
this.member = member;
}
public ModAnnotation(ElementType type, Type asmType, ModAnnotation parent)
{
this.type = type;
this.asmType = asmType;
}
@Override
public String toString()
{
return MoreObjects.toStringHelper("Annotation")
.add("type",type)
.add("name",asmType.getClassName())
.add("member",member)
.add("values", values)
.toString();
}
public ElementType getType()
{
return type;
}
public Type getASMType()
{
return asmType;
}
public String getMember()
{
return member;
}
public Map<String, Object> getValues()
{
return values;
}
public void addArray(String name)
{
this.arrayList = Lists.newArrayList();
this.arrayName = name;
}
public void addProperty(String key, Object value)
{
if (this.arrayList != null)
{
arrayList.add(value);
}
else
{
values.put(key, value);
}
}
public void addEnumProperty(String key, String enumName, String value)
{
addProperty(key, new EnumHolder(enumName, value));
}
public void endArray()
{
values.put(arrayName, arrayList);
arrayList = null;
}
public ModAnnotation addChildAnnotation(String name, String desc)
{
ModAnnotation child = new ModAnnotation(ElementType.PARAMETER, Type.getType(desc), this);
addProperty(name, child.getValues());
return child;
}
}
@@ -1,95 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import java.util.LinkedList;
public class ModAnnotationVisitor extends AnnotationVisitor
{
private final ModAnnotation annotation;
private LinkedList<ModAnnotation> annotations;
private boolean array;
private String name;
private boolean isSubAnnotation;
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation)
{
super(Opcodes.ASM5);
this.annotations = annotations;
this.annotation = annotation;
}
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, String name)
{
this(annotations, annotation);
this.array = true;
this.name = name;
annotation.addArray(name);
}
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, boolean isSubAnnotation)
{
this(annotations, annotation);
this.isSubAnnotation = true;
}
@Override
public void visit(String key, Object value)
{
annotation.addProperty(key, value);
}
@Override
public void visitEnum(String name, String desc, String value)
{
annotation.addEnumProperty(name, desc, value);
}
@Override
public AnnotationVisitor visitArray(String name)
{
return new ModAnnotationVisitor(annotations, annotation, name);
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc)
{
ModAnnotation ma = annotations.getFirst();
final ModAnnotation childAnnotation = ma.addChildAnnotation(name, desc);
annotations.addFirst(childAnnotation);
return new ModAnnotationVisitor(annotations, childAnnotation,true);
}
@Override
public void visitEnd()
{
if (array)
{
annotation.endArray();
}
if (isSubAnnotation)
{
ModAnnotation child = annotations.removeFirst();
annotations.addLast(child);
}
}
}
@@ -1,86 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.fml.language.ModFileScanData;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.annotation.ElementType;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModClassVisitor extends ClassVisitor
{
private Type asmType;
private Type asmSuperType;
private Set<Type> interfaces;
private final LinkedList<ModAnnotation> annotations = new LinkedList<>();
public ModClassVisitor()
{
super(Opcodes.ASM5);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
{
this.asmType = Type.getObjectType(name);
this.asmSuperType = superName != null && superName.length() > 0 ? Type.getObjectType(superName) : null;
this.interfaces = Stream.of(interfaces).map(Type::getObjectType).collect(Collectors.toSet());
}
@Override
public AnnotationVisitor visitAnnotation(final String annotationName, final boolean runtimeVisible)
{
ModAnnotation ann = new ModAnnotation(ElementType.TYPE, Type.getType(annotationName), this.asmType.getClassName());
annotations.addFirst(ann);
return new ModAnnotationVisitor(annotations, ann);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
{
return new ModFieldVisitor(name, annotations);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
return new ModMethodVisitor(name, desc, annotations);
}
public void buildData(final List<ModFileScanData.ClassData> classes, final List<ModFileScanData.AnnotationData> annotations) {
classes.add(new ModFileScanData.ClassData(this.asmType, this.asmSuperType, this.interfaces));
final List<ModFileScanData.AnnotationData> collect = this.annotations.stream().
filter(ma->ModFileScanData.interestingAnnotations().test(ma.asmType)).
map(a -> ModAnnotation.fromModAnnotation(this.asmType, a)).collect(Collectors.toList());
annotations.addAll(collect);
}
}
@@ -1,82 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import cpw.mods.modlauncher.ServiceLoaderStreamUtils;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.ModSorter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.SCAN;
public class ModDiscoverer {
private static final Logger LOGGER = LogManager.getLogger();
private final ServiceLoader<IModLocator> locators;
private final List<IModLocator> locatorList;
public ModDiscoverer() {
locators = ServiceLoader.load(IModLocator.class);
locatorList = ServiceLoaderStreamUtils.toList(this.locators);
LOGGER.debug(SCAN,"Found Mod Locators : {}", ()->locatorList.stream().map(iModLocator -> "("+iModLocator.name() + ":" + iModLocator.getClass().getPackage().getImplementationVersion()+")").collect(Collectors.joining(",")));
}
ModDiscoverer(List<IModLocator> locatorList) {
this.locatorList = locatorList;
this.locators = null;
}
public BackgroundScanHandler discoverMods() {
LOGGER.debug(SCAN,"Scanning for mods and other resources to load. We know {} ways to find mods", locatorList.size());
final Map<ModFile.Type, List<ModFile>> modFiles = locatorList.stream()
.peek(loc -> LOGGER.debug(SCAN,"Trying locator {}", loc))
.map(IModLocator::scanMods)
.flatMap(Collection::stream)
.peek(mf -> LOGGER.debug(SCAN,"Found mod file {} of type {} with locator {}", mf.getFileName(), mf.getType(), mf.getLocator()))
.collect(Collectors.groupingBy(ModFile::getType));
FMLLoader.getLanguageLoadingProvider().addAdditionalLanguages(modFiles.get(ModFile.Type.LANGPROVIDER));
BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler();
final List<ModFile> mods = modFiles.getOrDefault(ModFile.Type.MOD, Collections.emptyList());
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext(); )
{
ModFile mod = iterator.next();
if (!mod.identifyMods()) {
LOGGER.debug(SCAN, "Removing file {} from further analysis - it is invalid", mod);
iterator.remove();
}
}
LOGGER.debug(SCAN,"Found {} mod files with {} mods", mods::size, ()->mods.stream().mapToInt(mf -> mf.getModInfos().size()).sum());
final LoadingModList loadingModList = ModSorter.sort(mods);
loadingModList.addCoreMods();
loadingModList.addAccessTransformers();
loadingModList.addForScanning(backgroundScanHandler);
return backgroundScanHandler;
}
}
@@ -1,49 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.annotation.ElementType;
import java.util.LinkedList;
public class ModFieldVisitor extends FieldVisitor
{
private final LinkedList<ModAnnotation> annotations;
private final String fieldName;
public ModFieldVisitor(String name, final LinkedList<ModAnnotation> annotations)
{
super(Opcodes.ASM5);
this.fieldName = name;
this.annotations = annotations;
}
@Override
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible)
{
ModAnnotation ann = new ModAnnotation(ElementType.FIELD, Type.getType(annotationName), fieldName);
annotations.addFirst(ann);
return new ModAnnotationVisitor(annotations, ann);
}
}
@@ -1,186 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import com.google.common.collect.ImmutableMap;
import net.minecraftforge.fml.language.IModFileInfo;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.language.ModFileScanData;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.language.IModLanguageProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.Logging.SCAN;
public class ModFile
{
private static final Manifest DEFAULTMANIFEST;
private static final Logger LOGGER = LogManager.getLogger();
static {
DEFAULTMANIFEST = new Manifest();
DEFAULTMANIFEST.getMainAttributes().putValue("FMLModType", "MOD");
}
private final String jarVersion;
private Map<String, Object> fileProperties;
private IModLanguageProvider loader;
public void setFileProperties(Map<String, Object> fileProperties)
{
this.fileProperties = fileProperties;
}
public IModLanguageProvider getLoader()
{
return loader;
}
public Path findResource(String className)
{
return locator.findPath(this, className);
}
public void identifyLanguage() {
this.loader = FMLLoader.getLanguageLoadingProvider().findLanguage(this, this.modFileInfo.getModLoader(), this.modFileInfo.getModLoaderVersion());
}
public enum Type {
MOD, LIBRARY, LANGPROVIDER
}
private final Path filePath;
private final Type modFileType;
private final Manifest manifest;
private final IModLocator locator;
private IModFileInfo modFileInfo;
private ModFileScanData fileModFileScanData;
private CompletableFuture<ModFileScanData> futureScanResult;
private List<CoreModFile> coreMods;
private Path accessTransformer;
private static final Attributes.Name TYPE = new Attributes.Name("FMLModType");
public ModFile(final Path file, final IModLocator locator) {
this.locator = locator;
this.filePath = file;
manifest = locator.findManifest(file).orElse(DEFAULTMANIFEST);
if (manifest != DEFAULTMANIFEST) LOGGER.debug(SCAN,"Mod file {} has a manifest", file);
else LOGGER.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;
}
public Path getFilePath() {
return filePath;
}
public List<IModInfo> getModInfos() {
return modFileInfo.getMods();
}
public Optional<Path> getAccessTransformer() {
return Optional.ofNullable(Files.exists(accessTransformer) ? accessTransformer : null);
}
public boolean identifyMods() {
this.modFileInfo = ModFileParser.readModList(this);
if (this.modFileInfo == null) return false;
LOGGER.debug(LOADING,"Loading mod file {} with language {}", this.getFilePath(), this.modFileInfo.getModLoader());
this.coreMods = ModFileParser.getCoreMods(this);
this.coreMods.forEach(mi-> LOGGER.debug(LOADING,"Found coremod {}", mi.getPath()));
this.accessTransformer = locator.findPath(this, "META-INF", "accesstransformer.cfg");
return true;
}
public List<CoreModFile> getCoreMods() {
return coreMods;
}
/**
* Run in an executor thread to harvest the class and annotation list
*/
public ModFileScanData compileContent() {
return new Scanner(this).scan();
}
public void scanFile(Consumer<Path> pathConsumer) {
locator.scanFile(this, pathConsumer);
}
public void setFutureScanResult(CompletableFuture<ModFileScanData> future)
{
this.futureScanResult = future;
}
public ModFileScanData getScanResult() {
if (this.futureScanResult != null) {
try {
this.futureScanResult.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return this.fileModFileScanData;
}
public void setScanResult(final ModFileScanData modFileScanData, final Throwable throwable) {
this.futureScanResult = null;
this.fileModFileScanData = modFileScanData;
}
@Override
public String toString() {
return "Mod File: " + Objects.toString(this.filePath);
}
public String getFileName() {
return getFilePath().getFileName().toString();
}
public IModLocator getLocator() {
return locator;
}
public IModFileInfo getModFileInfo() {
return modFileInfo;
}
}
@@ -1,102 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import net.minecraftforge.fml.loading.MavenVersionAdapter;
import net.minecraftforge.fml.language.IModFileInfo;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.StringUtils;
import org.apache.maven.artifact.versioning.VersionRange;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ModFileInfo implements IModFileInfo
{
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<IModInfo> mods;
private final Map<String,Object> properties;
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(MavenVersionAdapter::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);
}
@Override
public List<IModInfo> getMods()
{
return mods;
}
public ModFile getFile()
{
return this.modFile;
}
@Override
public UnmodifiableConfig getConfig()
{
return this.config;
}
@Override
public URL getUpdateURL(IModFileInfo modFileInfo)
{
return this.updateJSONURL;
}
@Override
public String getModLoader()
{
return modLoader;
}
@Override
public VersionRange getModLoaderVersion()
{
return modLoaderVersion;
}
}
@@ -1,82 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import com.electronwill.nightconfig.core.file.FileConfig;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import net.minecraftforge.fml.language.IModFileInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.LOADING;
public class ModFileParser {
private static final Logger LOGGER = LogManager.getLogger();
public static IModFileInfo readModList(final ModFile modFile) {
LOGGER.debug(LOADING,"Parsing mod file candidate {}", modFile.getFilePath());
final Path modsjson = modFile.getLocator().findPath(modFile, "META-INF", "mods.toml");
if (!Files.exists(modsjson)) {
LOGGER.warn(LOADING, "Mod file {} is missing mods.toml file", modFile);
return null;
}
return loadModFile(modFile, modsjson);
}
public static IModFileInfo loadModFile(final ModFile file, final Path modsjson)
{
final FileConfig fileConfig = FileConfig.builder(modsjson).build();
fileConfig.load();
fileConfig.close();
return new ModFileInfo(file, fileConfig);
}
protected static List<CoreModFile> getCoreMods(final ModFile modFile) {
Map<String,String> coreModPaths;
try {
final Path coremodsjson = modFile.getLocator().findPath(modFile, "META-INF", "coremods.json");
if (!Files.exists(coremodsjson)) {
return Collections.emptyList();
}
final Type type = new TypeToken<Map<String, String>>() {}.getType();
final Gson gson = new Gson();
coreModPaths = gson.fromJson(Files.newBufferedReader(coremodsjson), type);
} catch (IOException e) {
LOGGER.debug(LOADING,"Failed to read coremod list coremods.json", e);
return Collections.emptyList();
}
return coreModPaths.entrySet().stream().
peek(e-> LOGGER.debug(LOADING,"Found coremod {} with Javascript path {}", e.getKey(), e.getValue())).
map(e -> new CoreModFile(e.getKey(), modFile.getLocator().findPath(modFile, e.getValue()),modFile)).
collect(Collectors.toList());
}
}
@@ -1,114 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.StringSubstitutor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
public class ModInfo implements IModInfo
{
private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1");
private final ModFileInfo owningFile;
private final String modId;
private final ArtifactVersion version;
private final String displayName;
private final String description;
private final List<IModInfo.ModVersion> dependencies;
private final Map<String,Object> properties;
private final UnmodifiableConfig modConfig;
public ModInfo(final ModFileInfo owningFile, final UnmodifiableConfig modConfig)
{
this.owningFile = owningFile;
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();
}
}
@Override
public ModFileInfo getOwningFile() {
return owningFile;
}
@Override
public String getModId() {
return modId;
}
@Override
public String getDisplayName()
{
return this.displayName;
}
@Override
public String getDescription()
{
return this.description;
}
@Override
public ArtifactVersion getVersion() {
return version;
}
@Override
public List<IModInfo.ModVersion> getDependencies() {
return this.dependencies;
}
@Override
public UnmodifiableConfig getModConfig() {
return this.modConfig;
}
public Optional<String> getLogoFile()
{
return this.owningFile != null ? this.owningFile.getConfig().getOptional("logoFile") : this.modConfig.getOptional("logoFile");
}
public boolean hasConfigUI()
{
return false;
}
}
@@ -1,52 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.annotation.ElementType;
import java.util.LinkedList;
public class ModMethodVisitor extends MethodVisitor {
private final LinkedList<ModAnnotation> annotations;
private String methodName;
private String methodDescriptor;
public ModMethodVisitor(String name, String desc, final LinkedList<ModAnnotation> annotations)
{
super(Opcodes.ASM5);
this.methodName = name;
this.methodDescriptor = desc;
this.annotations = annotations;
}
@Override
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible)
{
ModAnnotation ann = new ModAnnotation(ElementType.METHOD, Type.getType(annotationName), methodName+methodDescriptor);
annotations.addFirst(ann);
return new ModAnnotationVisitor(annotations, ann);
}
}
@@ -1,129 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import static cpw.mods.modlauncher.api.LamdbaExceptionUtils.uncheck;
import static net.minecraftforge.fml.Logging.SCAN;
/**
* Support loading mods located in JAR files in the mods folder
*/
public class ModsFolderLocator implements IModLocator {
private static final String SUFFIX = ".jar";
private static final Logger LOGGER = LogManager.getLogger();
private final Path modFolder;
private final Map<ModFile, FileSystem> modJars;
public ModsFolderLocator() {
this(FMLPaths.MODSDIR.get());
}
ModsFolderLocator(Path modFolder) {
this.modFolder = modFolder;
this.modJars = new HashMap<>();
}
@Override
public List<ModFile> scanMods() {
LOGGER.debug(SCAN,"Scanning mods dir {} for mods", this.modFolder);
return uncheck(()-> Files.list(this.modFolder)).
sorted(Comparator.comparing(path-> StringUtils.toLowerCase(path.getFileName().toString()))).
filter(p->StringUtils.toLowerCase(p.getFileName().toString()).endsWith(SUFFIX)).
map(p->new ModFile(p, this)).
peek(f->modJars.compute(f, (mf, fs)->createFileSystem(mf))).
collect(Collectors.toList());
}
@Override
public String name() {
return "mods folder";
}
private FileSystem createFileSystem(ModFile modFile) {
try {
return FileSystems.newFileSystem(modFile.getFilePath(), modFile.getClass().getClassLoader());
} catch (ZipError | IOException e) {
LOGGER.debug(SCAN,"Ignoring invalid JAR file {}", modFile.getFilePath());
return null;
}
}
@Override
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
return modJars.get(modFile).getPath(path[0], Arrays.copyOfRange(path, 1, path.length));
}
@Override
public void scanFile(final ModFile file, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scan started: {}", file);
FileSystem fs = modJars.get(file);
fs.getRootDirectories().forEach(path -> {
try (Stream<Path> files = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
});
LOGGER.debug(SCAN,"Scan finished: {}", file);
}
@Override
public String toString() {
return "{ModJarsFolder locator at "+this.modFolder+"}";
}
@Override
public Optional<Manifest> findManifest(final Path file)
{
try (JarFile jf = new JarFile(file.toFile()))
{
return Optional.ofNullable(jf.getManifest());
}
catch (IOException e)
{
return Optional.empty();
}
}
}
@@ -1,62 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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;
import net.minecraftforge.fml.language.IModLanguageProvider;
import net.minecraftforge.fml.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static net.minecraftforge.fml.Logging.SCAN;
public class Scanner {
private static final Logger LOGGER = LogManager.getLogger();
private final ModFile fileToScan;
public Scanner(final ModFile fileToScan) {
this.fileToScan = fileToScan;
}
public ModFileScanData scan() {
ModFileScanData result = new ModFileScanData();
fileToScan.scanFile(p -> fileVisitor(p, result));
final IModLanguageProvider loader = fileToScan.getLoader();
LOGGER.debug(SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name());
loader.getFileVisitor().accept(result);
return result;
}
private void fileVisitor(final Path path, final ModFileScanData result) {
try {
LOGGER.debug(SCAN,"Scanning {} path {}", fileToScan, path);
ModClassVisitor mcv = new ModClassVisitor();
ClassReader cr = new ClassReader(Files.newInputStream(path));
cr.accept(mcv, 0);
mcv.buildData(result.getClasses(), result.getAnnotations());
} catch (IOException e) {
// mark path bad
}
}
}
@@ -37,13 +37,11 @@ import java.util.stream.Collectors;
public class ResourcePackLoader
{
private static Map<ModFile, ModFileResourcePack> modResourcePacks;
private static AbstractResourcePack forgePack;
private static ResourcePackList<?> resourcePackList;
public static IResourcePack getResourcePackFor(String modId)
{
if (modId == "forge") return forgePack;
else return modResourcePacks.get(ModList.get().getModFileById(modId).getFile());
return modResourcePacks.get(ModList.get().getModFileById(modId).getFile());
}
@SuppressWarnings("unchecked")
@@ -56,9 +54,6 @@ public class ResourcePackLoader
modResourcePacks = ModList.get().getModFiles().stream().
map(mf -> new ModFileResourcePack(mf.getFile())).
collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity()));
forgePack = Files.isDirectory(FMLLoader.getForgePath()) ?
new ForgeFolderPack(FMLLoader.getForgePath().toFile()) :
new ForgeFilePack(FMLLoader.getForgePath().toFile());
resourcePacks.addPackFinder(new ModPackFinder());
}
@@ -111,8 +106,7 @@ public class ResourcePackLoader
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> packList, ResourcePackInfo.IFactory<T> factory)
{
packList.put("forge", ResourcePackInfo.func_195793_a("forge", true, ()->forgePack, factory, ResourcePackInfo.Priority.BOTTOM));
for (Entry<ModFile, ModFileResourcePack> e : modResourcePacks.entrySet())
for (Entry<ModFile, ModFileResourcePack> e : modResourcePacks.entrySet())
{
String name = "modfile/" + e.getKey().getFileName();
packList.put(name, ResourcePackInfo.func_195793_a(name, true, ()->e.getValue(), factory, ResourcePackInfo.Priority.BOTTOM));