From 6867ed50e62855e7d2a307ae50c947dab41b5e7b Mon Sep 17 00:00:00 2001 From: Jason Mitchell Date: Sat, 6 Jul 2024 12:19:16 -0700 Subject: [PATCH] Add mixins --- gradle.properties | 6 +- .../structurelib/core/StructureLibCore.java | 53 +++++ .../structurelib/mixins/Mixins.java | 191 ++++++++++++++++++ .../structurelib/mixins/TargetedMod.java | 30 +++ .../resources/mixins.structurelib.early.json | 8 + src/main/resources/mixins.structurelib.json | 8 + .../resources/mixins.structurelib.late.json | 8 + 7 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/gtnewhorizon/structurelib/core/StructureLibCore.java create mode 100644 src/main/java/com/gtnewhorizon/structurelib/mixins/Mixins.java create mode 100644 src/main/java/com/gtnewhorizon/structurelib/mixins/TargetedMod.java create mode 100644 src/main/resources/mixins.structurelib.early.json create mode 100644 src/main/resources/mixins.structurelib.json create mode 100644 src/main/resources/mixins.structurelib.late.json diff --git a/gradle.properties b/gradle.properties index 3843bb65..446d0563 100644 --- a/gradle.properties +++ b/gradle.properties @@ -83,7 +83,7 @@ apiPackage = accessTransformersFile = # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! -usesMixins = false +usesMixins = true # Adds some debug arguments like verbose output and class export. usesMixinDebug = false @@ -92,12 +92,12 @@ usesMixinDebug = false mixinPlugin = # Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! -mixinsPackage = +mixinsPackage = mixins # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # This parameter is for legacy compatibility only # Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin -coreModClass = +coreModClass = core.StructureLibCore # If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class # that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! diff --git a/src/main/java/com/gtnewhorizon/structurelib/core/StructureLibCore.java b/src/main/java/com/gtnewhorizon/structurelib/core/StructureLibCore.java new file mode 100644 index 00000000..17533eb5 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/core/StructureLibCore.java @@ -0,0 +1,53 @@ +package com.gtnewhorizon.structurelib.core; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; +import com.gtnewhorizon.structurelib.mixins.Mixins; + +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; + +public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader { + + public static final Logger log = LogManager.getLogger("StructureLibCore"); + + @Override + public String getMixinConfig() { + return "mixins.structurelib.early.json"; + } + + @Override + public List getMixins(Set loadedCoreMods) { + return Mixins.getEarlyMixins(loadedCoreMods); + } + + @Override + public String[] getASMTransformerClass() { + return new String[0]; + } + + @Override + public String getModContainerClass() { + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) { + + } + + @Override + public String getAccessTransformerClass() { + return null; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/mixins/Mixins.java b/src/main/java/com/gtnewhorizon/structurelib/mixins/Mixins.java new file mode 100644 index 00000000..7f93fed8 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/mixins/Mixins.java @@ -0,0 +1,191 @@ +package com.gtnewhorizon.structurelib.mixins; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import com.gtnewhorizon.structurelib.core.StructureLibCore; + +import cpw.mods.fml.relauncher.FMLLaunchHandler; + +public enum Mixins { + + EMPTY(new Builder("Empty").setPhase(Phase.EARLY).addMixinClasses("com.gtnewhorizon.structurelib.mixins.EmptyMixin") + .addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH).setApplyIf(() -> false)); + // DISABLE_MODDED_CHUNK_POPULATION(new Builder("Disable all other mod chunk population (e.g. Natura clouds") + // .setPhase(Phase.EARLY).addMixinClasses("minecraft.MixinChunkProviderServer_DisableModGeneration") + // .addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH) + // .setApplyIf(() -> TweaksConfig.disableModdedChunkPopulation)); + + private final List mixinClasses; + private final List targetedMods; + private final List excludedMods; + private final Supplier applyIf; + private final Phase phase; + private final Side side; + + Mixins(Builder builder) { + this.mixinClasses = builder.mixinClasses; + this.targetedMods = builder.targetedMods; + this.excludedMods = builder.excludedMods; + this.applyIf = builder.applyIf; + this.phase = builder.phase; + this.side = builder.side; + if (this.mixinClasses.isEmpty()) { + throw new RuntimeException("No mixin class specified for Mixin : " + this.name()); + } + if (this.targetedMods.isEmpty()) { + throw new RuntimeException("No targeted mods specified for Mixin : " + this.name()); + } + if (this.applyIf == null) { + throw new RuntimeException("No ApplyIf function specified for Mixin : " + this.name()); + } + if (this.phase == null) { + throw new RuntimeException("No Phase specified for Mixin : " + this.name()); + } + if (this.side == null) { + throw new RuntimeException("No Side function specified for Mixin : " + this.name()); + } + } + + public static List getEarlyMixins(Set loadedCoreMods) { + final List mixins = new ArrayList<>(); + final List notLoading = new ArrayList<>(); + for (Mixins mixin : Mixins.values()) { + if (mixin.phase == Phase.EARLY) { + if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) { + mixins.addAll(mixin.mixinClasses); + } else { + notLoading.addAll(mixin.mixinClasses); + } + } + } + StructureLibCore.log.info("Not loading the following EARLY mixins: {}", notLoading.toString()); + return mixins; + } + + public static List getLateMixins(Set loadedMods) { + // NOTE: Any targetmod here needs a modid, not a coremod id + final List mixins = new ArrayList<>(); + final List notLoading = new ArrayList<>(); + for (Mixins mixin : Mixins.values()) { + if (mixin.phase == Phase.LATE) { + if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) { + mixins.addAll(mixin.mixinClasses); + } else { + notLoading.addAll(mixin.mixinClasses); + } + } + } + StructureLibCore.log.info("Not loading the following LATE mixins: {}", notLoading.toString()); + return mixins; + } + + private boolean shouldLoadSide() { + return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer()) + || (side == Side.CLIENT && FMLLaunchHandler.side().isClient()); + } + + private boolean allModsLoaded(List targetedMods, Set loadedCoreMods, Set loadedMods) { + if (targetedMods.isEmpty()) return false; + + for (TargetedMod target : targetedMods) { + if (target == TargetedMod.VANILLA) continue; + + // Check coremod first + if (!loadedCoreMods.isEmpty() && target.coreModClass != null + && !loadedCoreMods.contains(target.coreModClass)) + return false; + else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false; + } + + return true; + } + + private boolean noModsLoaded(List targetedMods, Set loadedCoreMods, Set loadedMods) { + if (targetedMods.isEmpty()) return true; + + for (TargetedMod target : targetedMods) { + if (target == TargetedMod.VANILLA) continue; + + // Check coremod first + if (!loadedCoreMods.isEmpty() && target.coreModClass != null + && loadedCoreMods.contains(target.coreModClass)) + return false; + else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false; + } + + return true; + } + + private boolean shouldLoad(Set loadedCoreMods, Set loadedMods) { + return (shouldLoadSide() && applyIf.get() + && allModsLoaded(targetedMods, loadedCoreMods, loadedMods) + && noModsLoaded(excludedMods, loadedCoreMods, loadedMods)); + } + + private static class Builder { + + private final String name; + private final List mixinClasses = new ArrayList<>(); + private final List targetedMods = new ArrayList<>(); + private final List excludedMods = new ArrayList<>(); + private Supplier applyIf = null; + private Phase phase = null; + private Side side = null; + + public Builder(String name) { + this.name = name; + } + + public Builder addMixinClasses(String... mixinClasses) { + this.mixinClasses.addAll(Arrays.asList(mixinClasses)); + return this; + } + + public Builder setPhase(Phase phase) { + if (this.phase != null) { + throw new RuntimeException("Trying to define Phase twice for " + this.name); + } + this.phase = phase; + return this; + } + + public Builder setSide(Side side) { + if (this.side != null) { + throw new RuntimeException("Trying to define Side twice for " + this.name); + } + this.side = side; + return this; + } + + public Builder setApplyIf(Supplier applyIf) { + this.applyIf = applyIf; + return this; + } + + public Builder addTargetedMod(TargetedMod mod) { + this.targetedMods.add(mod); + return this; + } + + public Builder addExcludedMod(TargetedMod mod) { + this.excludedMods.add(mod); + return this; + } + } + + private enum Side { + BOTH, + CLIENT, + SERVER + } + + private enum Phase { + EARLY, + LATE, + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/mixins/TargetedMod.java b/src/main/java/com/gtnewhorizon/structurelib/mixins/TargetedMod.java new file mode 100644 index 00000000..f6f00aeb --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/mixins/TargetedMod.java @@ -0,0 +1,30 @@ +package com.gtnewhorizon.structurelib.mixins; + +import cpw.mods.fml.common.Mod; + +public enum TargetedMod { + + VANILLA("Minecraft", null); + + /** The "name" in the {@link Mod @Mod} annotation */ + public final String modName; + /** Class that implements the IFMLLoadingPlugin interface */ + public final String coreModClass; + /** The "modid" in the {@link Mod @Mod} annotation */ + public final String modId; + + TargetedMod(String modName, String coreModClass) { + this(modName, coreModClass, null); + } + + TargetedMod(String modName, String coreModClass, String modId) { + this.modName = modName; + this.coreModClass = coreModClass; + this.modId = modId; + } + + @Override + public String toString() { + return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}"; + } +} diff --git a/src/main/resources/mixins.structurelib.early.json b/src/main/resources/mixins.structurelib.early.json new file mode 100644 index 00000000..f1e3a562 --- /dev/null +++ b/src/main/resources/mixins.structurelib.early.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.8.5-GTNH", + "package": "com.gtnewhorizon.structurelib.mixins.early", + "refmap": "mixins.structurelib.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/main/resources/mixins.structurelib.json b/src/main/resources/mixins.structurelib.json new file mode 100644 index 00000000..dc73681b --- /dev/null +++ b/src/main/resources/mixins.structurelib.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.8.5-GTNH", + "package": "com.gtnewhorizon.structurelib.mixins", + "refmap": "mixins.structurelib.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/main/resources/mixins.structurelib.late.json b/src/main/resources/mixins.structurelib.late.json new file mode 100644 index 00000000..091cde29 --- /dev/null +++ b/src/main/resources/mixins.structurelib.late.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.8.5-GTNH", + "package": "com.gtnewhorizon.structurelib.mixins.late", + "refmap": "mixins.structurelib.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +}