mirror of
https://github.com/LegacyModdingMC/UniMixins.git
synced 2026-10-27 06:53:48 +00:00
Compare commits
7
Commits
devastation
...
0.1.23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a6265993e | ||
|
|
dbf46b5445 | ||
|
|
84aabd1c6c | ||
|
|
4cf9abd071 | ||
|
|
9e2cd35b5a | ||
|
|
092d0843d1 | ||
|
|
55b9660726 |
No files matched your search
+1
-1
@@ -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) {
|
||||
|
||||
+1
@@ -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);
|
||||
}
|
||||
+167
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -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]);
|
||||
}
|
||||
}
|
||||
+136
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -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;
|
||||
}
|
||||
}
|
||||
+101
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-29
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-22
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
mixinExtrasVersion=0.4.1
|
||||
mixinExtrasVersion=0.5.0
|
||||
Reference in new issue
Block a user