package gregtech.mixin; import static gregtech.mixin.TargetedMod.VANILLA; 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 org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import bartworks.common.configs.Configuration; import cpw.mods.fml.relauncher.FMLLaunchHandler; public enum Mixin { // Minecraft SoundManagerMixin(new Builder("Seeking sound playback") .addMixinClasses("minecraft.SoundManagerMixin", "minecraft.SoundManagerInnerMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> true) .setPhase(Phase.EARLY) .setSide(Side.CLIENT)), WorldMixin(new Builder("Block update detection").addMixinClasses("minecraft.WorldMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> true) .setPhase(Phase.EARLY) .setSide(Side.BOTH)), StringTranslateMixin(new Builder("Keep track of currently translating mods") .addMixinClasses("minecraft.StringTranslateMixin", "minecraft.LanguageRegistryMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> true) .setPhase(Phase.EARLY) .setSide(Side.BOTH)), LocaleMixin(new Builder("Keep track of currently translating client mods").addMixinClasses("minecraft.LocaleMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> true) .setPhase(Phase.EARLY) .setSide(Side.CLIENT)), CacheCraftingManagerRecipes( new Builder("Cache CraftingManager recipes").addMixinClasses("minecraft.CraftingManagerMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> Configuration.mixins.enableCraftingManagerRecipeCaching) .setPhase(Phase.EARLY) .setSide(Side.BOTH)), VanillaAccessors(new Builder("Adds various accessors") .addMixinClasses( "minecraft.accessors.BlockStemMixin", "minecraft.accessors.VanillaShapedRecipeMixin", "minecraft.accessors.VanillaShapelessRecipeMixin", "minecraft.accessors.ForgeShapedRecipeMixin", "minecraft.accessors.ForgeShapelessRecipeMixin", "minecraft.accessors.PotionMixin", "minecraft.accessors.EntityPlayerMPMixin") .addTargetedMod(VANILLA) .setApplyIf(() -> true) .setPhase(Phase.EARLY) .setSide(Side.BOTH)), IC2_MACHINE_WRENCHING(new Builder("Changes the behavior of the wrenching mechanic for IC2 machines") .addMixinClasses("ic2.MixinDamageDropped", "ic2.MixinHarvestTool", "ic2.MixinItemDropped") .addTargetedMod(TargetedMod.IC2) .setApplyIf(() -> true) .setPhase(Phase.LATE) .setSide(Side.BOTH)); public static final Logger LOGGER = LogManager.getLogger("GregTech-Mixin"); private final List mixinClasses; private final List targetedMods; private final List excludedMods; private final Supplier applyIf; private final Phase phase; private final Side side; Mixin(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 (Mixin mixin : Mixin.values()) { if (mixin.phase == Phase.EARLY) { if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) { mixins.addAll(mixin.mixinClasses); } else { notLoading.addAll(mixin.mixinClasses); } } } LOGGER.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 (Mixin mixin : Mixin.values()) { if (mixin.phase == Phase.LATE) { if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) { mixins.addAll(mixin.mixinClasses); } else { notLoading.addAll(mixin.mixinClasses); } } } LOGGER.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, } }