Compare commits

...
7 Commits
23 changed files with 1049 additions and 74 deletions

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ buildscript {
}
}
dependencies {
classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.11'
classpath 'com.anatawa12.forge:ForgeGradle:1.2-1.1.+'
}
}
@@ -10,5 +10,6 @@ import cpw.mods.fml.common.Mod;
version = Tags.VERSION,
name = Tags.MODNAME,
acceptableRemoteVersions = "*")
@Deprecated
public class GasStation {
}
@@ -3,14 +3,9 @@ package com.falsepattern.gasstation;
import java.util.List;
/**
* Early mixins are defined as mixins that affects vanilla or forge classes.
* Or technically, classes that can be queried via the current state of {@link net.minecraft.launchwrapper.LaunchClassLoader}
* <p>
* If you want to add mixins that affect mods, use {@link ILateMixinLoader}
* <p>
* Implement this in your {@link cpw.mods.fml.relauncher.IFMLLoadingPlugin}.
* Return all early mixin configs you want MixinBooter to queue and send to Mixin library.
* Use {@link com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader} instead!
*/
@Deprecated
public interface IEarlyMixinLoader {
/**
@@ -2,17 +2,11 @@ package com.falsepattern.gasstation;
import java.util.List;
/**
* Late mixins are defined as mixins that affects mod classes.
* Or technically, classes that can be queried via the current state of
* {@link net.minecraft.launchwrapper.LaunchClassLoader}
* <p>
* Majority if not all vanilla and forge classes would have been loaded here.
* If you want to add mixins that affect vanilla or forge, use and consult {@link IEarlyMixinLoader}
* <p>
* Implement this in any arbitrary class. Said class will be constructed when mixins are ready to be queued.
* Return all late mixin configs you want MixinBooter to queue and send to Mixin library.
* Use {@link com.gtnewhorizon.gtnhmixins.ILateMixinLoader} instead!
*/
@Deprecated
public interface ILateMixinLoader {
/**
@@ -21,6 +21,7 @@ import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Path;
@Deprecated
public class MinecraftURLClassPath {
/**
* Utility to manipulate the minecraft URL ClassPath
@@ -2,6 +2,7 @@ package com.falsepattern.gasstation;
// Use this class for Strings only. Do not import any classes here. It will lead to issues with Mixins if in use!
@Deprecated
public class Tags {
// GRADLETOKEN_* will be replaced by your configuration values at build time
@@ -22,6 +22,7 @@ import java.util.Map;
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 5)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.Name(GasStationCore.PLUGIN_NAME)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions("com.falsepattern.gasstation.core")
@Deprecated
public class GasStationCore implements IFMLLoadingPlugin {
public static final String PLUGIN_NAME = Tags.MODNAME + " Core Plugin";
public static final Logger LOGGER = LogManager.getLogger(PLUGIN_NAME);
@@ -8,6 +8,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
@Deprecated
public class DevMixinPlugin implements IMixinConfigPlugin {
@Override
public void onLoad(String mixinPackage) {
@@ -4,6 +4,7 @@ import cpw.mods.fml.common.discovery.ModCandidate;
import java.util.List;
@Deprecated
public interface IModDiscovererMixin {
List<ModCandidate> getCandidates();
}
@@ -1,5 +1,6 @@
package com.gtnewhorizon.gtnhmixins;
import com.gtnewhorizon.gtnhmixins.core.GTNHMixinsCore;
import cpw.mods.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -11,4 +12,26 @@ public class GTNHMixins {
public static final String MODID = "gtnhmixins";
public static final String VERSION = "GRADLETOKEN_VERSION";
public static final Logger LOGGER = LogManager.getLogger(NAME);
/**
* Logs at level info in de-obfuscated environment, level debug otherwise
*/
public static void log(String message) {
if (GTNHMixinsCore.isObf()) {
LOGGER.debug(message);
} else {
LOGGER.info(message);
}
}
/**
* Logs at level info in de-obfuscated environment, level debug otherwise
*/
public static void log(String message, Object... params) {
if (GTNHMixinsCore.isObf()) {
LOGGER.debug(message, params);
} else {
LOGGER.info(message, params);
}
}
}
@@ -1,5 +1,6 @@
package com.gtnewhorizon.gtnhmixins;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Set;
@@ -11,7 +12,7 @@ import java.util.Set;
* If you want to add mixins that affect vanilla or forge, use and consult {@link IEarlyMixinLoader}
* <p>
* Implement this in any arbitrary class, along with the annotation @{@link LateMixin}. The annotation will cause the class to be constructed when mixins are
* ready to be queued. Return all of the mixin classes (under the mixinConfig's package) that you want to queue and send to Mixin library.
* ready to be queued. Return additional mixin classes if you want to dynamically enable or disable them, or an empty list if there is none.
*/
public interface ILateMixinLoader {
@@ -22,7 +23,7 @@ public interface ILateMixinLoader {
/**
* @param loadedMods Set of loaded modids, for use in discrimination of what mixins load
* @return mixin configurations to be queued and sent to Mixin library.
* @return additional mixin configurations to be queued and sent to Mixin library.
*/
List<String> getMixins(Set<String> loadedMods);
}
@Nonnull List<String> getMixins(Set<String> loadedMods);
}
@@ -0,0 +1,167 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
@SuppressWarnings("ForLoopReplaceableByForEach")
public abstract class AbstractBuilder {
protected @Nullable List<String> commonClasses;
protected @Nullable List<String> clientClasses;
protected @Nullable List<String> serverClasses;
protected @Nullable List<ITargetMod> requiredMods;
protected @Nullable List<ITargetMod> excludedMods;
protected @Nullable Phase phase;
protected @Nonnull Supplier<Boolean> applyIf = () -> true;
protected AbstractBuilder addCommonClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (commonClasses == null) commonClasses = new ArrayList<>(4);
Collections.addAll(commonClasses, classes);
return this;
}
protected AbstractBuilder addClientClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (clientClasses == null) clientClasses = new ArrayList<>(4);
Collections.addAll(clientClasses, classes);
return this;
}
protected AbstractBuilder addServerClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (serverClasses == null) serverClasses = new ArrayList<>(4);
Collections.addAll(serverClasses, classes);
return this;
}
protected AbstractBuilder addSidedClasses(@Nonnull Side side, @Nonnull String... classes) {
Objects.requireNonNull(side);
switch (side) {
case COMMON:
return addCommonClasses(classes);
case CLIENT:
return addClientClasses(classes);
case SERVER:
return addServerClasses(classes);
default:
throw new IllegalArgumentException();
}
}
protected AbstractBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
Objects.requireNonNull(applyIf);
this.applyIf = applyIf;
return this;
}
protected AbstractBuilder addRequiredMod(@Nonnull ITargetMod mod) {
Objects.requireNonNull(mod);
if (requiredMods == null) requiredMods = new ArrayList<>(2);
requiredMods.add(mod);
return this;
}
protected AbstractBuilder addExcludedMod(@Nonnull ITargetMod mod) {
Objects.requireNonNull(mod);
if (excludedMods == null) excludedMods = new ArrayList<>(2);
excludedMods.add(mod);
return this;
}
protected void addClassesForCurrentSide(List<String> toLoad, List<String> toNotLoad) {
final boolean isClient = FMLLaunchHandler.side().isClient();
if (commonClasses != null) toLoad.addAll(commonClasses);
if (clientClasses != null) {
if (isClient) toLoad.addAll(clientClasses);
else toNotLoad.addAll(clientClasses);
}
if (serverClasses != null) {
if (!isClient) toLoad.addAll(serverClasses);
else toNotLoad.addAll(serverClasses);
}
}
protected void addAllClassesTo(List<String> list) {
if (commonClasses != null) list.addAll(commonClasses);
if (clientClasses != null) list.addAll(clientClasses);
if (serverClasses != null) list.addAll(serverClasses);
}
protected void addAllTargetsTo(Set<ITargetMod> set) {
if (requiredMods != null) set.addAll(requiredMods);
if (excludedMods != null) set.addAll(excludedMods);
}
protected boolean shouldLoad(Set<ITargetMod> loadedTargets) {
return allRequiredModsPresent(loadedTargets) && noExcludedModsPresent(loadedTargets);
}
protected boolean allRequiredModsPresent(Set<ITargetMod> loadedTargets) {
if (requiredMods == null) return true;
for (int i = 0; i < requiredMods.size(); i++) {
if (!loadedTargets.contains(requiredMods.get(i))) return false;
}
return true;
}
protected boolean noExcludedModsPresent(Set<ITargetMod> loadedTargets) {
if (excludedMods == null) return true;
for (int i = 0; i < excludedMods.size(); i++) {
if (loadedTargets.contains(excludedMods.get(i))) return false;
}
return true;
}
protected static void loadClasses(List<AbstractBuilder> builders, Set<ITargetMod> loadedTargets, List<String> toLoad, List<String> toNotLoad) {
for (int i = 0; i < builders.size(); i++) {
AbstractBuilder builder = builders.get(i);
if (builder.shouldLoad(loadedTargets)) {
builder.addClassesForCurrentSide(toLoad, toNotLoad);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
}
protected static Set<ITargetMod> getLoadedTargetedMods(List<AbstractBuilder> builders, Phase loadingPhase, Set<String> loadedCoreMods, Set<String> loadedMods) {
Set<ITargetMod> targets = new HashSet<>();
for (int i = 0; i < builders.size(); i++) {
builders.get(i).addAllTargetsTo(targets);
}
Iterator<ITargetMod> iterator = targets.iterator();
List<ITargetMod> notPresent = new ArrayList<>();
while (iterator.hasNext()) {
ITargetMod target = iterator.next();
TargetModBuilder builder = target.getBuilder();
TargetModBuilder.validateBuilder(builder, target, loadingPhase);
if (!builder.isTargetPresent(loadedCoreMods, loadedMods)) {
notPresent.add(target);
iterator.remove();
}
}
if (loadingPhase == null) {
GTNHMixins.log("ITargetMods found: {}", targets.toString());
GTNHMixins.log("ITargetMods not found: {}", notPresent.toString());
} else {
GTNHMixins.log("ITargetMods found during Phase {}: {}", loadingPhase, targets.toString());
GTNHMixins.log("ITargetMods not found during Phase {}: {}", loadingPhase, notPresent.toString());
}
return targets;
}
}
@@ -0,0 +1,18 @@
package com.gtnewhorizon.gtnhmixins.builders;
public interface IBaseTransformer {
/**
* Phase is only used for early and late mixins from gtnh mixins
*/
enum Phase {
EARLY,
LATE
}
enum Side {
COMMON,
CLIENT,
SERVER
}
}
@@ -0,0 +1,141 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* The IMixins interface provides a flexible way of declaring and registering mixins during runtime.
* Registration is done based on conditions such as user defined logic, looking configs, loading side (CLIENT, COMMON, SERVER)
* or looking at the presence or absence of mods declared using the {@link com.gtnewhorizon.gtnhmixins.builders.ITargetMod} interface.
* This interface must be implemented on an enum.
* <pre>
* {@code
* public enum Mixins implements IMixins {
*
* EXAMPLE_MIXIN(new MixinBuilder("Hello") // optional comment
* .addRequiredMod(TargetMod.IC2) // optional
* .addExcludedMod(...) // optional
* .setPhase(Phase.EARLY) // optional, required for gtnh mixins
* .addCommonMixins("........") // mixins that needs to be both on client and server
* .addClientMixins("........") // mixins only for the client
* .addServerMixins("........") // mixins only for the dedicated server
* .setApplyIf(() -> Config.yourconfig....)); // optional
* // it is required to have at least one mixin class
*
* private final MixinBuilder builder;
*
* Mixins(MixinBuilder builder) {
* this.builder = builder;
* }
*
* @Override
* public MixinBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
* <p>
* If you do not need very complex logic in your enum, you can instantiate
* the MixinBuilder in the enum constructor and declare simplistic enum entries.
*
* <pre>
* {@code
* public enum Mixins implements IMixins {
*
* CLIENT_MIXIN1(Side.CLIENT, "MixinClientClass1"),
* CLIENT_MIXIN2(Side.CLIENT, "MixinClientClass2"),
* COMMON_MIXIN(Side.COMMON, "MixinCommonClass");
*
* private final MixinBuilder builder;
*
* Mixins(Side side, String... mixins) {
* builder = new MixinBuilder().addSidedMixins(side, mixins);
* }
*
* @Override
* public MixinBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
*/
@SuppressWarnings("unused")
public interface IMixins extends IBaseTransformer {
@Nonnull
MixinBuilder getBuilder();
/**
* Returns the list of mixins that should be loaded by your {@link org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin} implementation.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins() {
* return IMixins.getMixins(YourMixinEnum.class);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getMixins(Class<E> mixinsEnum) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadMixins(mixinsEnum, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following mixins: {}", toNotLoad);
for (String mixin : toLoad) {
GTNHMixins.log("Loading {}", mixin);
}
return toLoad;
}
/**
* Returns the list of mixins that should be loaded by your {@link com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader} implementation.
* Note that you also need to implement {@link cpw.mods.fml.relauncher.IFMLLoadingPlugin} for early mixins to work.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins(Set<String> loadedCoreMods) {
* return IMixins.getEarlyMixins(YourMixinEnum.class, loadedCoreMods);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getEarlyMixins(Class<E> mixinsEnum, Set<String> loadedCoreMods) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadEarlyMixins(mixinsEnum, loadedCoreMods, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following EARLY mixins: {}", toNotLoad);
return toLoad;
}
/**
* Returns the list of mixins that should be loaded by your {@link com.gtnewhorizon.gtnhmixins.ILateMixinLoader} implementation.
* Note that you also need to annotate your class with {@link com.gtnewhorizon.gtnhmixins.LateMixin} for late mixins to work.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins(Set<String> loadedMods) {
* return IMixins.getLateMixins(YourMixinEnum.class, loadedMods);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getLateMixins(Class<E> mixinsEnum, Set<String> loadedMods) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadLateMixins(mixinsEnum, loadedMods, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following LATE mixins: {}", toNotLoad.toString());
return toLoad;
}
}
@@ -0,0 +1,127 @@
package com.gtnewhorizon.gtnhmixins.builders;
import net.minecraftforge.fml.common.versioning.ComparableVersion;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.function.Predicate;
/**
* This interface is used to identify mods targeted by the {@link com.gtnewhorizon.gtnhmixins.builders.IMixins}
* and {@link com.gtnewhorizon.gtnhmixins.builders.ITransformers} system. It provides a flexible mechanism for
* conditionally applying mixins or asm transformers based on the presence or absence of other mods during runtime.
* You can declare your targeted mods in an enum, although it's not required to be an enum.
* <pre>
* {@code
* public enum TargetMods implements ITargetMod {
*
* COFHCORE("cofh.asm.LoadingPlugin", "CoFHCore"),
* OPTIFINE("optifine.OptiFineForgeTweaker", "Optifine");
*
* private final TargetModBuilder builder;
*
* TargetMods(String coreModClass, String modId) {
* this.builder = new TargetModBuilder().setCoreModClass(coreModClass).setModId(modId);
* }
*
* @Override
* public getBuidler() {
* return builder;
* }
* }
* }
* </pre>
*/
public interface ITargetMod {
@Nonnull
TargetModBuilder getBuilder();
class Predicates {
public static Predicate<String> equals(String str) {
return s -> s.equals(str);
}
public static Predicate<String> contains(String str) {
return s -> s.contains(str);
}
public static Predicate<String> endsWith(String str) {
return s -> s.endsWith(str);
}
public static Predicate<String> startsWith(String str) {
return s -> s.startsWith(str);
}
public static Predicate<String> matches(String str) {
return s -> s.matches(str);
}
/**
* Returns a predicate that is true if the version provided is greater than the version it is compared to.
*/
public static Predicate<String> versionGreater(String version) {
return s -> new ComparableVersion(version).compareTo(new ComparableVersion(s)) > 0;
}
/**
* Returns a predicate that is true if the version provided is lower than the version it is compared to.
*/
public static Predicate<String> versionLower(String version) {
return s -> new ComparableVersion(version).compareTo(new ComparableVersion(s)) < 0;
}
/**
* If the class targeted by this test has a {@link cpw.mods.fml.common.Mod} annotation, this test will parse the annotation and
* test it against the predicates. At least one of the predicates must be non-null, this test will return true if all the predicates are true.
*/
public static Predicate<ClassNode> testModAnnotation(Predicate<String> modIdTest, Predicate<String> modNameTest, Predicate<String> modVersionTest) {
if (modIdTest == null && modNameTest == null && modVersionTest == null) {
throw new IllegalArgumentException("At least one of the Mod Annotation test predicates must be non null");
}
return cn -> {
if (cn.visibleAnnotations == null) return false;
for (AnnotationNode annotation : cn.visibleAnnotations) {
if ("Lcpw/mods/fml/common/Mod;".equals(annotation.desc)) {
if (annotation.values != null) {
String modId = "";
String modName = "";
String modVersion = "";
final List<Object> values = annotation.values;
final int size = values.size();
for (int i = 0; i < size - 1; i += 2) {
Object name = values.get(i);
Object value = values.get(i + 1);
if (name instanceof String && value instanceof String) {
if ("modid".equals(name)) {
modId = (String) value;
} else if ("name".equals(name)) {
modName = (String) value;
} else if ("version".equals(name)) {
modVersion = (String) value;
}
}
}
boolean test = true;
if (modIdTest != null) {
test = /*test &&*/ modIdTest.test(modId);
}
if (modNameTest != null) {
test = test && modNameTest.test(modName);
}
if (modVersionTest != null) {
test = test && modVersionTest.test(modVersion);
}
return test;
}
}
}
return false;
};
}
}
}
@@ -0,0 +1,95 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
/**
* The ITransformers interface provides a flexible way of declaring and registering {@link net.minecraft.launchwrapper.IClassTransformer} during runtime.
* Registration is done based on conditions such as user defined logic, looking configs, loading side (CLIENT, COMMON, SERVER)
* or looking at the presence or absence of mods declared using the {@link com.gtnewhorizon.gtnhmixins.builders.ITargetMod} interface.
* This interface must be implemented on an enum.
* <pre>
* {@code
* public enum ASMTransformers implements ITransformers {
*
* EXAMPLE_TRANSFORMER(new TransformerBuilder("Hello") // optional comment
* .addRequiredMod(TargetMod.IC2) // optional
* .addExcludedMod(...) // optional
* .addCommonTransformers("........") // transformers that needs to be both on client and server
* .addClientTransformers("........") // transformers only for the client
* .addServerTransformers("........") // transformers only for the dedicated server
* .setApplyIf(() -> Config.yourconfig....)); // optional
* // it is required to have at least one transformers class
*
* private final TransformerBuilder builder;
*
* ASMTransformers(TransformerBuilder builder) {
* this.builder = builder;
* }
*
* @Override
* public TransformerBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
* <p>
* If you do not need very complex logic in your enum, you can instantiate
* the TransformerBuilder in the enum constructor and declare simplistic enum entries.
*
* <pre>
* {@code
* public enum ASMTransformers implements ITransformers {
*
* CLIENT_TRANSFORMER1(Side.CLIENT, "TransformerClientClass1"),
* CLIENT_TRANSFORMER2(Side.CLIENT, "TransformerClientClass2"),
* COMMON_TRANSFORMER(Side.COMMON, "TransformerCommonClass");
*
* private final TransformerBuilder builder;
*
* ASMTransformers(Side side, String... transformers) {
* builder = new TransformerBuilder().addSidedTransformers(side, transformers);
* }
*
* @Override
* public TransformerBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
*/
@SuppressWarnings("unused")
public interface ITransformers extends IBaseTransformer {
@Nonnull
TransformerBuilder getBuilder();
/**
* Returns the list of transformers that should be loaded by your {@link cpw.mods.fml.relauncher.IFMLLoadingPlugin} implementation.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public String[] getASMTransformerClass() {
* return ITransformers.getTransformers(YourTransformerEnum.class);
* }
* }
* </pre>
*/
static <E extends Enum<E> & ITransformers> String[] getTransformers(Class<E> transformerEnum) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
TransformerBuilder.loadTransformers(transformerEnum, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following transformers: {}", toNotLoad);
for (String transformer : toLoad) {
GTNHMixins.log("Loading ITransformer {}", transformer);
}
return toLoad.toArray(new String[0]);
}
}
@@ -0,0 +1,136 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
@SuppressWarnings("unused")
public class MixinBuilder extends AbstractBuilder {
public MixinBuilder() {}
public MixinBuilder(String description) {}
/**
* Add mixin classes that should be loaded both on the client and server
*/
public MixinBuilder addCommonMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addCommonClasses(mixins);
}
/**
* Add mixin classes that should only be loaded on the client
*/
public MixinBuilder addClientMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addClientClasses(mixins);
}
/**
* Add mixin classes that should only be loaded on the dedicated server
*/
public MixinBuilder addServerMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addServerClasses(mixins);
}
/**
* Add mixins classes that should be loaded on the specified side
*/
public MixinBuilder addSidedMixins(@Nonnull Side side, @Nonnull String... mixins) {
return (MixinBuilder) super.addSidedClasses(side, mixins);
}
/**
* Specify a condition that needs to be true for this mixin to load, such as checking for a config boolean.
*/
@Override
public MixinBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
return (MixinBuilder) super.setApplyIf(applyIf);
}
/**
* Specify mods that are required to be present for this mixin to load
*/
@Override
public MixinBuilder addRequiredMod(@Nonnull ITargetMod mod) {
return (MixinBuilder) super.addRequiredMod(mod);
}
/**
* Specify mods that will disable this mixin if they are present
*/
@Override
public MixinBuilder addExcludedMod(@Nonnull ITargetMod mod) {
return (MixinBuilder) super.addExcludedMod(mod);
}
/**
* Mixins registered from a {@link com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader} need to set this to
* {@link com.gtnewhorizon.gtnhmixins.builders.IMixins.Phase#EARLY}.
* <p>
* Mixins registered from a {@link com.gtnewhorizon.gtnhmixins.ILateMixinLoader} need to set this to
* {@link com.gtnewhorizon.gtnhmixins.builders.IMixins.Phase#LATE}.
* <p>
* Mixins registered from a {@link org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin} need to leave this
* null.
*/
public MixinBuilder setPhase(Phase phase) {
this.phase = phase;
return this;
}
protected static <E extends Enum<E> & IMixins> void loadMixins(Class<E> mixinsEnum, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, null, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, null, Collections.emptySet(), Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
protected static <E extends Enum<E> & IMixins> void loadEarlyMixins(Class<E> mixinsEnum, Set<String> loadedCoreMods, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, Phase.EARLY, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, Phase.EARLY, loadedCoreMods, Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
protected static <E extends Enum<E> & IMixins> void loadLateMixins(Class<E> mixinsEnum, Set<String> loadedMods, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, Phase.LATE, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, Phase.LATE, Collections.emptySet(), loadedMods);
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
private static <E extends Enum<E> & IMixins> List<AbstractBuilder> getEnabledBuildersForPhase(Class<E> mixinsEnum, Phase loadingPhase, List<String> toNotLoad) {
final E[] constants = mixinsEnum.getEnumConstants();
List<AbstractBuilder> list = new ArrayList<>(constants.length + 1);
for (E mixin : constants) {
MixinBuilder builder = mixin.getBuilder();
validateBuilder(builder, mixin, loadingPhase != null);
if (builder.phase != loadingPhase) continue;
if (builder.applyIf.get()) {
list.add(builder);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
return list;
}
private static void validateBuilder(MixinBuilder builder, Enum<?> mixin, boolean requirePhase) {
if (builder == null) {
throw new NullPointerException("Builder is null for IMixins : " + mixin.name());
}
int count = 0;
if (builder.commonClasses != null) count += builder.commonClasses.size();
if (builder.clientClasses != null) count += builder.clientClasses.size();
if (builder.serverClasses != null) count += builder.serverClasses.size();
if (count == 0) {
throw new IllegalArgumentException("No mixin class registered for IMixins : " + mixin.name());
}
if (requirePhase && builder.phase == null) {
throw new IllegalArgumentException("No Phase specified for IMixins : " + mixin.name());
}
}
}
@@ -0,0 +1,162 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.service.MixinService;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Set;
import java.util.function.Predicate;
@SuppressWarnings("unused")
public class TargetModBuilder implements ITargetMod {
private String coreModClass;
private String modId;
private String targetClass;
private Predicate<ClassNode> classNodeTest;
/**
* Fully qualified name of the class that implements the IFMLLoadingPlugin interface in this targeted mod. This can
* only be used by GTNH mixins early mixins system.
* <p>
* For example : "com.gtnewhorizons.angelica.loading.AngelicaTweaker"
*/
public TargetModBuilder setCoreModClass(String coreModClass) {
this.coreModClass = coreModClass;
return this;
}
/**
* The "modid" of the targeted mod, found in the @Mod(modid=) annotation. This can only be used by GTNH mixins late
* mixins system.
*/
public TargetModBuilder setModId(String modId) {
this.modId = modId;
return this;
}
/**
* Fully qualified name of a class of your choice contained in the targeted mod. Typically, its main mod class.
* <p>
* For example : "com.gtnewhorizons.angelica.AngelicaMod"
*/
public TargetModBuilder setTargetClass(String targetClass) {
this.targetClass = targetClass;
return this;
}
/**
* A conditional check that will test the raw (un-transformed) bytecode of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
*/
public TargetModBuilder setClassNodeTest(Predicate<ClassNode> classNodeTest) {
this.classNodeTest = classNodeTest;
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId of the target class is equals to the modId provided.
*/
public TargetModBuilder testModID(String modId) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, null);
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId and modVersion of the target class are equal to the modId and modVersion provided.
*/
public TargetModBuilder testModVersion(String modId, String modVersion) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, Predicates.equals(modVersion));
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId of the target class is equals to the modId provided and tests the modVersion with the provided test.
*/
public TargetModBuilder testModVersion(String modId, Predicate<String> modVersionTest) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, modVersionTest);
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Some arguments can be null but not all at once.
*/
public TargetModBuilder testModAnnotation(Predicate<String> modIdTest, Predicate<String> modNameTest, Predicate<String> modVersionTest) {
this.classNodeTest = Predicates.testModAnnotation(modIdTest, modNameTest, modVersionTest);
return this;
}
protected static void validateBuilder(TargetModBuilder builder, ITargetMod target, Phase phaseIn) {
if (builder == null) {
throw new NullPointerException("TargetModBuilder is null for ITargetMod " + target);
}
if (builder.modId == null && builder.coreModClass == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("No information at all provided by ITargetMod " + target);
}
if (phaseIn == Phase.EARLY) {
if (builder.coreModClass == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target + " used by early mixins");
}
} else if (phaseIn == Phase.LATE) {
if (builder.modId == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target + " used by late mixins");
}
} else {
if (builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target);
}
}
if (builder.classNodeTest != null && builder.targetClass == null) {
throw new IllegalArgumentException("ITargetMod " + target + " uses a ClassNode test but doesn't specify the target class");
}
}
private boolean foundTargetClass;
protected boolean isTargetPresent(Set<String> loadedCoreMods, Set<String> loadedMods) {
// 1. check coremod class (early mixins only)
if (!loadedCoreMods.isEmpty() && this.coreModClass != null && loadedCoreMods.contains(this.coreModClass)) {
return true;
}
// 2. check modID (late mixins only)
if (!loadedMods.isEmpty() && this.modId != null && loadedMods.contains(this.modId)) {
return true;
}
// 3. check class
if (this.targetClass != null) {
if (foundTargetClass) {
return true;
}
try {
ClassNode classNode = MixinService.getService().getBytecodeProvider().getClassNode(this.targetClass, false);
if (this.classNodeTest == null) {
foundTargetClass = true;
return true;
}
// 4. test bytecode of this class
final boolean test = this.classNodeTest.test(classNode);
if (test) {
foundTargetClass = true;
return true;
}
} catch (ClassNotFoundException | IOException ignored) {}
}
return false;
}
@Nonnull
@Override
public TargetModBuilder getBuilder() {
return this;
}
}
@@ -0,0 +1,101 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class TransformerBuilder extends AbstractBuilder {
public TransformerBuilder() {}
public TransformerBuilder(String description) {}
/**
* Add transformer classes that should be loaded both on the client and server
*/
public TransformerBuilder addCommonTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addCommonClasses(transformers);
}
/**
* Add transformer classes that should only be loaded on the client
*/
public TransformerBuilder addClientTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addClientClasses(transformers);
}
/**
* Add transformer classes that should only be loaded on the dedicated server
*/
public TransformerBuilder addServerTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addServerClasses(transformers);
}
/**
* Add transformer classes that should be loaded on the specified side
*/
public TransformerBuilder addSidedTransformers(@Nonnull Side side, @Nonnull String... transformers) {
return (TransformerBuilder) super.addSidedClasses(side, transformers);
}
/**
* Specify a condition that needs to be true for this transformer to load, such as checking for a config boolean.
*/
@Override
public TransformerBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
return (TransformerBuilder) super.setApplyIf(applyIf);
}
/**
* Specify mods that are required to be present for this transformer to load
*/
public TransformerBuilder addRequiredMod(@Nonnull ITargetMod mod) {
return (TransformerBuilder) super.addRequiredMod(mod);
}
/**
* Specify mods that will disable this transformer if they are present
*/
public TransformerBuilder addExcludedMod(@Nonnull ITargetMod mod) {
return (TransformerBuilder) super.addExcludedMod(mod);
}
protected static <E extends Enum<E> & ITransformers> void loadTransformers(Class<E> transformerEnum, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(transformerEnum, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, null, Collections.emptySet(), Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
private static <E extends Enum<E> & ITransformers> List<AbstractBuilder> getEnabledBuildersForPhase(Class<E> transformerEnum, List<String> toNotLoad) {
final E[] constants = transformerEnum.getEnumConstants();
List<AbstractBuilder> list = new ArrayList<>(constants.length + 1);
for (E transformer : constants) {
TransformerBuilder builder = transformer.getBuilder();
validateBuilder(builder, transformer);
if (builder.applyIf.get()) {
list.add(builder);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
return list;
}
private static void validateBuilder(TransformerBuilder builder, Enum<?> transformer) {
if (builder == null) {
throw new NullPointerException("Builder is null for ITransformer : " + transformer.name());
}
int count = 0;
if (builder.commonClasses != null) count += builder.commonClasses.size();
if (builder.clientClasses != null) count += builder.clientClasses.size();
if (builder.serverClasses != null) count += builder.serverClasses.size();
if (count == 0) {
throw new IllegalArgumentException("No transformer class registered for ITransformer : " + transformer.name());
}
}
}
@@ -2,16 +2,16 @@ package com.gtnewhorizon.gtnhmixins.core;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Runnables;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.Reflection;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import io.github.legacymoddingmc.unimixins.gtnhmixins.GTNHMixinsModule;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.transformer.Config;
import io.github.legacymoddingmc.unimixins.gtnhmixins.GTNHMixinsModule;
import org.spongepowered.asm.service.MixinService;
import java.util.ArrayList;
import java.util.HashSet;
@@ -29,23 +29,23 @@ import java.util.Set;
public class GTNHMixinsCore implements IFMLLoadingPlugin {
public static final String PLUGIN_NAME = "GTNHMixins Core Plugin";
public static final Logger LOGGER = LogManager.getLogger(PLUGIN_NAME);
private static boolean isObf;
/**
* Core mods we need to Manually look for:
* (classNameToFind, coreModNameToUse)
* (classNameToFind, coreModNameToUse)
*/
public static final Map<String, String> MANUALLY_IDENTIFIED_COREMODS = ImmutableMap.<String, String>builder()
.put("optifine.OptiFineForgeTweaker", "optifine.OptiFineForgeTweaker")
.put("codechicken.lib.asm.ModularASMTransformer", "codechicken.lib")
.put("org.bukkit.World", "Bukkit")
.build();
.put("optifine.OptiFineForgeTweaker", "optifine.OptiFineForgeTweaker")
.put("codechicken.lib.asm.ModularASMTransformer", "codechicken.lib")
.put("org.bukkit.World", "Bukkit")
.build();
static {
LOGGER.info("Initializing GTNHMixins Core");
GTNHMixins.LOGGER.debug("Initializing GTNHMixins Core");
GTNHMixinsModule.init();
}
@Override
public String[] getASMTransformerClass() {
return new String[0];
@@ -63,23 +63,22 @@ public class GTNHMixinsCore implements IFMLLoadingPlugin {
private Set<String> getLoadedCoremods(List<?> coremodList) {
final Set<String> loadedCoremods = new HashSet<>();
// Manual "Coremod" identification. Be sure you're not loading classes too early
MANUALLY_IDENTIFIED_COREMODS.forEach((clsName, coreModIdentifier) -> {
try {
Class.forName(clsName);
MixinService.getService().getBytecodeProvider().getClassNode(clsName, false);
loadedCoremods.add(coreModIdentifier);
} catch (ClassNotFoundException ignored) {}
} catch (Exception ignored) {}
});
// Grab a list of tweakers (fastcraft)
for (Object tweak : (ArrayList<?>)Launch.blackboard.get("Tweaks")) {
for (Object tweak : (ArrayList<?>) Launch.blackboard.get("Tweaks")) {
final Object obj;
try {
obj = Reflection.pluginWrapperClass.isInstance(tweak) ? Reflection.coreModInstanceField.get(tweak) : tweak;
obj = Reflection.pluginWrapperClass.isInstance(tweak) ? Reflection.coreModInstanceField.get(tweak) : tweak;
loadedCoremods.add(obj.toString().split("@")[0]);
} catch(Exception ignored) {}
} catch (Exception ignored) {}
}
// Now coremods
for (Object coremod : coremodList) {
@@ -90,43 +89,49 @@ public class GTNHMixinsCore implements IFMLLoadingPlugin {
}
return loadedCoremods;
}
@Override
public void injectData(Map<String, Object> data) {
LOGGER.debug("Examining core mod list");
final Object coremodList = data.get("coremodList");
isObf = (boolean) data.get("runtimeDeobfuscationEnabled");
GTNHMixins.log("Searching coremodList for IEarlyMixinLoaders");
final Object coremodList = data.get("coremodList");
if (coremodList instanceof List) {
final Set<String> loadedCoremods = getLoadedCoremods((List<?>) coremodList);
LOGGER.debug("LoadedCoreMods {}", loadedCoremods.toString());
for (Object coremod : (List<?>)coremodList) {
GTNHMixins.log("LoadedCoreMods {}", loadedCoremods.toString());
for (Object coremod : (List<?>) coremodList) {
// Identify any coremods that are `IEarlyMixinLoader`, and inject any relevant mixins
try {
Object theMod = Reflection.coreModInstanceField.get(coremod);
if (theMod instanceof IEarlyMixinLoader) {
final IEarlyMixinLoader loader = (IEarlyMixinLoader)theMod;
GTNHMixins.log("Loading mixins from IEarlyMixinLoader [{}]", theMod.getClass().getName());
final IEarlyMixinLoader loader = (IEarlyMixinLoader) theMod;
final String mixinConfig = loader.getMixinConfig();
final Config config = Config.create(mixinConfig, null);
final List<String> mixins = loader.getMixins(loadedCoremods);
for(String mixin : mixins) {
LOGGER.debug("Loading [{}] {}", mixinConfig, mixin);
for (String mixin : mixins) {
GTNHMixins.log("Loading [{}] {}", mixinConfig, mixin);
}
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
Reflection.registerConfigurationMethod.invoke(null, config);
}
} catch (Exception e) {
LOGGER.error("Unexpected error", e);
} catch (ReflectiveOperationException e) {
GTNHMixins.LOGGER.error("Unexpected error", e);
}
}
}
((Runnable)Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
((Runnable) Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
}
@Override
public String getAccessTransformerClass() {
return null;
}
public static boolean isObf() {
return isObf;
}
}
@@ -5,7 +5,6 @@ import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import com.gtnewhorizon.gtnhmixins.Reflection;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.LoaderState;
@@ -13,7 +12,6 @@ import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.discovery.ASMDataTable;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.MixinEnvironment;
@@ -25,6 +23,7 @@ import org.spongepowered.asm.mixin.transformer.Proxy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -35,6 +34,7 @@ import java.util.Set;
@Mixin(value = LoadController.class, remap = false)
public class LateMixinOrchestrationMixin {
@SuppressWarnings("unchecked")
@Inject(method = "distributeStateMessage(Lcpw/mods/fml/common/LoaderState;[Ljava/lang/Object;)V", at = @At(value = "HEAD"))
private void beforeConstructing(LoaderState state, Object[] eventData, CallbackInfo ci) throws Throwable {
// This state is where Forge adds mod files to ModClassLoader
@@ -42,43 +42,47 @@ public class LateMixinOrchestrationMixin {
return;
}
GTNHMixins.LOGGER.info("Instantiating all @LateMixin annotated and ILateMixinLoader implemented classes...");
GTNHMixins.log("Searching ASMData for ILateMixinLoaders");
ModClassLoader modClassLoader = (ModClassLoader)eventData[0];
ASMDataTable asmDataTable = (ASMDataTable)eventData[1];
ModClassLoader modClassLoader = (ModClassLoader) eventData[0];
ASMDataTable asmDataTable = (ASMDataTable) eventData[1];
final Loader loader = Loader.instance();
final Set<String> loadedModsTemp = new HashSet<>();
loadedModsTemp.addAll(loader.getIndexedModList().keySet());
loadedModsTemp.addAll(getLiteLoaderMods());
final Set<String> loadedMods = Collections.unmodifiableSet(loadedModsTemp);
GTNHMixins.LOGGER.info("LoadedMods {}", loadedMods.toString());
GTNHMixins.log("LoadedMods {}", loadedMods.toString());
for (ASMDataTable.ASMData asmData : asmDataTable.getAll(LateMixin.class.getName())) {
modClassLoader.addFile(asmData.getCandidate().getModContainer()); // Add to path before `newInstance`
final String mixinClassName = asmData.getClassName().replace('/', '.');
GTNHMixins.LOGGER.info("Instantiating {} for its mixins.", mixinClassName);
Class<?> lateMixinClass = Class.forName(mixinClassName);
if(!ILateMixinLoader.class.isAssignableFrom(lateMixinClass)) {
if (!ILateMixinLoader.class.isAssignableFrom(lateMixinClass)) {
GTNHMixins.LOGGER.error("Class {} has the @LateMixin annotation, but does not implement the ILateMixinLoader interface!", mixinClassName);
continue;
}
ILateMixinLoader lateLoader = (ILateMixinLoader)lateMixinClass.newInstance();
ILateMixinLoader lateLoader = (ILateMixinLoader) lateMixinClass.newInstance();
GTNHMixins.log("Loading mixins from ILateMixinLoader [{}]", lateLoader.getClass().getName());
final String mixinConfig = lateLoader.getMixinConfig();
GTNHMixins.LOGGER.info("Adding {} mixin configuration.", mixinConfig);
final Config config = Config.create(mixinConfig, null);
final List<String> mixins = lateLoader.getMixins(loadedMods);
for(String mixin : mixins) {
GTNHMixins.LOGGER.info("Loading [{}] {}", mixinConfig, mixin);
final Object o = Reflection.mixinClassesField.get(Reflection.configField.get(config));
final List<String> mixins;
if (o instanceof List) {
mixins = (List<String>) o;
} else {
mixins = new ArrayList<>();
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
}
mixins.addAll(lateLoader.getMixins(loadedMods));
for (String mixin : mixins) {
GTNHMixins.log("Loading [{}] {}", mixinConfig, mixin);
}
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
Reflection.registerConfigurationMethod.invoke(null, config);
}
((Runnable)Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
((Runnable) Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
// Add all mods to the classpath now so they're available for mixin
for (ModContainer container : loader.getActiveModList()) {
@@ -90,7 +94,7 @@ public class LateMixinOrchestrationMixin {
Field transformerField = Proxy.class.getDeclaredField("transformer");
transformerField.setAccessible(true);
@SuppressWarnings("OptionalGetWithoutIsPresent")
Object transformer = transformerField.get(Launch.classLoader.getTransformers().stream().filter(Proxy.class::isInstance).findFirst().get());
@@ -109,14 +113,14 @@ public class LateMixinOrchestrationMixin {
Object bootstrap = FieldUtils.readDeclaredField(instance, "bootstrap", true);
Object enumerator = FieldUtils.readDeclaredField(bootstrap, "enumerator", true);
Map<String, ?> enabledContainers = (Map<String, ?>) FieldUtils.readDeclaredField(enumerator, "enabledContainers", true);
GTNHMixins.LOGGER.info("LiteLoader present, adding its mods to the list.");
for(Entry<String, ?> e : enabledContainers.entrySet()) {
if((boolean) hasValidMetaData.invoke(e.getValue())) {
GTNHMixins.log("LiteLoader present, adding its mods to the list");
for (Entry<String, ?> e : enabledContainers.entrySet()) {
if ((boolean) hasValidMetaData.invoke(e.getValue())) {
mods.add(e.getKey());
}
}
} catch (ClassNotFoundException e) {
GTNHMixins.LOGGER.info("LiteLoader not present.");
GTNHMixins.log("LiteLoader not present");
} catch (Exception e) {
GTNHMixins.LOGGER.error("Failed to get LiteLoader mods.", e);
}
+1 -1
View File
@@ -4,4 +4,4 @@ spongepoweredMixinVersion=0.8.7
gtnhMixinVersion=0.8.7-GTNH-2
gasmixMixinVersion=0.8.7-gasstation_7
asmVersion=9.7.1
asmVersion=9.8
+1 -1
View File
@@ -1 +1 @@
mixinExtrasVersion=0.4.1
mixinExtrasVersion=0.5.0