diff --git a/build.gradle b/build.gradle index 26ce2f25d..8aa9876ee 100644 --- a/build.gradle +++ b/build.gradle @@ -137,6 +137,10 @@ task deobfJar(type: Jar) { } jar { + manifest { + attributes 'FMLCorePlugin': 'techreborn.asm.LoadingPlugin' + attributes 'FMLCorePluginContainsFMLMod': 'true' + } exclude "**/*.psd" appendix = 'universal' classifier = 'universal' diff --git a/src/main/java/techreborn/asm/ClassTransformation.java b/src/main/java/techreborn/asm/ClassTransformation.java new file mode 100644 index 000000000..34ece24ad --- /dev/null +++ b/src/main/java/techreborn/asm/ClassTransformation.java @@ -0,0 +1,179 @@ +package techreborn.asm; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModAPIManager; +import cpw.mods.fml.common.ModContainer; +import cpw.mods.fml.common.discovery.ASMDataTable; +import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException; +import cpw.mods.fml.common.versioning.VersionRange; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class ClassTransformation implements IClassTransformer { + + private static boolean scrappedData = false; + static String strippableDesc; + private static final String[] emptyList = {}; + + public ClassTransformation() { + strippableDesc = Type.getDescriptor(Strippable.class); + } + + + @Override + public byte[] transform(String name, String transformedName, byte[] bytes) { + + if (bytes == null) { + return null; + } + + ClassReader cr = new ClassReader(bytes); + ClassNode cn = new ClassNode(); + cr.accept(cn, 0); + if (strip(cn)) { + ClassWriter cw = new ClassWriter(0); + cn.accept(cw); + bytes = cw.toByteArray(); + System.out.println("Striped " + name); + } else { + //LogHelper.trace("Nothing stripped from " + transformedName); + } + + return bytes; + } + + static boolean strip(ClassNode cn) { + boolean altered = false; + if (cn.visibleAnnotations != null) { + for (AnnotationNode n : cn.visibleAnnotations) { + AnnotationInfo node = parseAnnotation(n, strippableDesc); + if(node != null){ + + } + } + } + if (cn.methods != null) { + Iterator iter = cn.methods.iterator(); + while (iter.hasNext()) { + MethodNode mn = iter.next(); + if (mn.visibleAnnotations != null) { + for (AnnotationNode node : mn.visibleAnnotations) { + if (checkRemove(parseAnnotation(node, strippableDesc), iter)) { + altered = true; + break; + } + } + } + } + } + if (cn.fields != null) { + Iterator iter = cn.fields.iterator(); + while (iter.hasNext()) { + FieldNode fn = iter.next(); + if (fn.visibleAnnotations != null) { + for (AnnotationNode node : fn.visibleAnnotations) { + if (checkRemove(parseAnnotation(node, strippableDesc), iter)) { + altered = true; + break; + } + } + } + } + } + return altered; + } + + + static AnnotationInfo parseAnnotation(AnnotationNode node, String desc) { + + AnnotationInfo info = null; + if (node.desc.equals(desc)) { + info = new AnnotationInfo(); + if (node.values != null) { + List values = node.values; + for (int i = 0, e = values.size(); i < e; ) { + Object k = values.get(i++); + Object v = values.get(i++); + if ("value".equals(k)) { + if (!(v instanceof List && ((List) v).size() > 0 && ((List) v).get(0) instanceof String)) { + continue; + } + info.values = ((List) v).toArray(emptyList); + } else if ("side".equals(k) && v instanceof String[]) { + String t = ((String[]) v)[1]; + if (t != null) { + info.side = t.toUpperCase().intern(); + } + } + } + } + } + return info; + } + + static boolean checkRemove(AnnotationInfo node, Iterator iter) { + + if (node != null) { + boolean needsRemoved = true; + if (!needsRemoved) { + String[] value = node.values; + for (int j = 0, l = value.length; j < l; ++j) { + String clazz = value[j]; + String mod = clazz.substring(4); + if (clazz.startsWith("mod:")) { + int i = mod.indexOf('@'); + if (i > 0) { + clazz = mod.substring(i + 1); + mod = mod.substring(0, i); + } + needsRemoved = !Loader.isModLoaded(mod); + if (!needsRemoved && i > 0) { + ModContainer modc = getLoadedMods().get(mod); + try { + needsRemoved = !VersionRange.createFromVersionSpec(clazz).containsVersion(modc.getProcessedVersion()); + } catch (InvalidVersionSpecificationException e) { + needsRemoved = true; + } + } + } + if (needsRemoved) { + break; + } + } + } + if (needsRemoved) { + iter.remove(); + return true; + } + } + return false; + } + + static class AnnotationInfo { + + public String side = "NONE"; + public String[] values = emptyList; + } + + private static Map mods; + static Map getLoadedMods() { + + if (mods == null) { + mods = new HashMap(); + for (ModContainer m : Loader.instance().getModList()) { + mods.put(m.getModId(), m); + } + } + return mods; + } +} \ No newline at end of file diff --git a/src/main/java/techreborn/asm/LoadingPlugin.java b/src/main/java/techreborn/asm/LoadingPlugin.java new file mode 100644 index 000000000..ca0b053dd --- /dev/null +++ b/src/main/java/techreborn/asm/LoadingPlugin.java @@ -0,0 +1,82 @@ +package techreborn.asm; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import cpw.mods.fml.common.DummyModContainer; +import cpw.mods.fml.common.LoadController; +import cpw.mods.fml.common.ModMetadata; +import cpw.mods.fml.common.discovery.ASMDataTable; +import cpw.mods.fml.common.event.FMLConstructionEvent; +import cpw.mods.fml.relauncher.IFMLCallHook; +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; +import techreborn.lib.ModInfo; + +import java.util.Map; + +@IFMLLoadingPlugin.MCVersion( "1.7.10" ) +public class LoadingPlugin implements IFMLLoadingPlugin { + + public static ASMDataTable ASM_DATA = null; + public static boolean runtimeDeobfEnabled = false; + + @Override + public String[] getASMTransformerClass() { + return new String[]{"techreborn.asm.ClassTransformation"}; + } + + @Override + public String getModContainerClass() { + return DummyMod.class.getName(); + } + + @Override + public String getSetupClass() { + return DummyMod.class.getName(); + } + + @Override + public void injectData(Map data) { + runtimeDeobfEnabled = (Boolean) data.get("runtimeDeobfuscationEnabled"); + } + + @Override + public String getAccessTransformerClass() { + return null; + } + + public static class DummyMod extends DummyModContainer implements IFMLCallHook { + + public DummyMod() { + + super(new ModMetadata()); + ModMetadata md = getMetadata(); + md.autogenerated = true; + md.modId = ModInfo.MOD_ID + "asm"; + md.name = md.description = "Techreborn-Stripper"; + md.parent = ModInfo.MOD_ID; + md.version = "000"; + } + + @Override + public boolean registerBus(EventBus bus, LoadController controller) { + + bus.register(this); + return true; + } + + @Subscribe + public void construction(FMLConstructionEvent evt) { + } + + @Override + public Void call() throws Exception { + + return null; + } + + @Override + public void injectData(Map data) { + + } + } +} diff --git a/src/main/java/techreborn/asm/Strippable.java b/src/main/java/techreborn/asm/Strippable.java new file mode 100644 index 000000000..ba226cdfb --- /dev/null +++ b/src/main/java/techreborn/asm/Strippable.java @@ -0,0 +1,23 @@ +package techreborn.asm; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * When used on a class, methods from referenced interfaces will not be removed
+ * When using this annotation on methods, ensure you do not switch on an enum inside that method. JavaC implementation details means this will cause crashes. + *

+ * Can also strip on modid using "mod:CoFHCore" as a value
+ * Can also strip on API using "api:CoFHAPI|energy" as a value + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.TYPE}) +public @interface Strippable { + + public String[] value(); + +} \ No newline at end of file diff --git a/src/main/java/techreborn/powerSystem/TilePowerAcceptor.java b/src/main/java/techreborn/powerSystem/TilePowerAcceptor.java index 62ec1a3b3..68fde439d 100644 --- a/src/main/java/techreborn/powerSystem/TilePowerAcceptor.java +++ b/src/main/java/techreborn/powerSystem/TilePowerAcceptor.java @@ -2,17 +2,50 @@ package techreborn.powerSystem; import cofh.api.energy.IEnergyReceiver; import cpw.mods.fml.common.Optional; +import ic2.api.energy.prefab.BasicSink; +import ic2.api.energy.tile.IEnergySink; import ic2.api.energy.tile.IEnergyTile; +import net.minecraft.tileentity.TileEntity; import techreborn.api.power.IEnergyInterfaceTile; +import techreborn.asm.Strippable; +import techreborn.tiles.TileMachineBase; -@Optional.Interface(iface="ic2.api.energy.tile.IEnergyTile", modid="IC2", striprefs=true) -public abstract class TilePowerAcceptor implements IEnergyReceiver, IEnergyInterfaceTile, IEnergyTile { - - +@Optional.InterfaceList(value={ + @Optional.Interface(iface="ic2.api.energy.tile.IEnergyTile", modid="IC2", striprefs=true), + @Optional.Interface(iface="ic2.api.energy.tile.IEnergySink", modid="IC2", striprefs=true) +}) +public abstract class TilePowerAcceptor extends TileMachineBase implements + IEnergyReceiver, //Techreborn + IEnergyInterfaceTile,//Cofh + IEnergyTile, IEnergySink //Ic2 +{ + @Strippable("mod:ic2") + BasicSink sink; + @Strippable("mod:ic2") + @Override + public void updateEntity() + { + super.updateEntity(); + sink.updateEntity(); + } + @Strippable("mod:ic2") + @Override + public void invalidate() + { + sink.invalidate(); + super.invalidate(); + } + @Strippable("mod:ic2") + @Override + public void onChunkUnload() + { + sink.onChunkUnload(); + super.onChunkUnload(); + } } diff --git a/src/main/java/techreborn/tiles/TileLathe.java b/src/main/java/techreborn/tiles/TileLathe.java index 367f77b31..76041b979 100644 --- a/src/main/java/techreborn/tiles/TileLathe.java +++ b/src/main/java/techreborn/tiles/TileLathe.java @@ -10,6 +10,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.ForgeDirection; import techreborn.api.recipe.RecipeCrafter; +import techreborn.asm.Strippable; import techreborn.init.ModBlocks; import techreborn.util.Inventory; @@ -33,7 +34,7 @@ public class TileLathe extends TileMachineBase implements IWrenchable, IEnergyTi outputs[0] = 1; crafter = new RecipeCrafter("latheRecipe", this, energy, 1, 1, inventory, inputs, outputs); } - + @Override public void updateEntity() {