diff --git a/README.md b/README.md
index e8ee9461..3c9aca9e 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,9 @@ Modify all the things, then do it again!
Melt down any metals you find.
Turn everything into golems!
+##Development
+Install Forge as usual, and setup your IDE as with any other Forge project. Forge Multipart must be installed to `forge/mcp/jars/mods/` from the [Forge file server](http://files.minecraftforge.net/ForgeMultipart/). Copy `TCore_dummy.jar` to `forge/mcp/jars/mods/` to enable the Preloader (optional -- only needed when working on the preloader itself)
+
##Compile from Source
Run [Apache Ant](http://ant.apache.org/bindownload.cgi) in the repository root: `ant package`
diff --git a/TCore_dummy.jar b/TCore_dummy.jar
new file mode 100644
index 00000000..3a33363d
Binary files /dev/null and b/TCore_dummy.jar differ
diff --git a/build.xml b/build.xml
index dbe52b05..b338b7ba 100644
--- a/build.xml
+++ b/build.xml
@@ -24,22 +24,26 @@
+
+
+
+
@@ -65,6 +69,7 @@
+
@@ -78,6 +83,7 @@
+
@@ -91,8 +97,8 @@
-
+
@@ -205,8 +211,9 @@
-
-
+
+
+
@@ -224,6 +231,7 @@
+
@@ -241,7 +249,7 @@
-
+
diff --git a/resources/PreloaderManifest.mf b/resources/PreloaderManifest.mf
new file mode 100644
index 00000000..9318a994
--- /dev/null
+++ b/resources/PreloaderManifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+FMLCorePlugin: tconstruct.preloader.TConstructLoaderContainer
+FMLCorePluginContainsFMLMod: true
diff --git a/resources/mcmod.info b/resources/mcmod.info
index 098e0072..5a094e7f 100644
--- a/resources/mcmod.info
+++ b/resources/mcmod.info
@@ -16,6 +16,23 @@
"parent":"",
"dependencies": []
},
+ {
+ "modid": "TConstruct-Preloader",
+ "name": "Tinkers Corestruct",
+ "description": "Preloader for Tinkers Construct",
+ "version": "0.0.1",
+ "mcversion": "@MCVERSION@",
+ "url": "https://github.com/mDiyo/TinkersConstruct/tree/master/src/tconstruct/preloader",
+ "updateUrl": "",
+ "authors": [
+ "AlgorithmX2", "Sunstrike"
+ ],
+ "credits": "",
+ "logoFile": "",
+ "screenshots": [],
+ "parent":"",
+ "dependencies": []
+ },
{
"modid": "TConstruct|CompatMineFactoryReloaded",
"name": "TConstruct Compat: MFR",
@@ -34,8 +51,8 @@
"dependencies": [
"TConstruct"
]
- }
- {
+ },
+ {
"modid": "TConstruct|CompatForgeMultiPart",
"name": "TConstruct Compat: FMP",
"description": "TConstruct support for forgemultipart",
diff --git a/src/tconstruct/preloader/ASMInterfaceRepair.java b/src/tconstruct/preloader/ASMInterfaceRepair.java
new file mode 100644
index 00000000..8b5cbe72
--- /dev/null
+++ b/src/tconstruct/preloader/ASMInterfaceRepair.java
@@ -0,0 +1,157 @@
+package tconstruct.preloader;
+
+/*
+ * ASMInterfaceRepair is derived from code graciously provided by AlgorithmX2 of Applied Energistics.
+ * The code was adapted for this loader by Sunstrike. Any queries should be directed to him, not AlgorithmX2.
+ */
+
+import net.minecraft.launchwrapper.IClassTransformer;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class ASMInterfaceRepair implements IClassTransformer {
+
+ public static Map APIInterfaces = new HashMap();
+
+ @Override
+ public byte[] transform(String name, String transformedName, byte[] bytes)
+ {
+ if ( name.startsWith( "tconstruct." ) && ! name.startsWith( "tconstruct.preloader" ) )
+ {
+ ClassReader cr = new ClassReader(bytes);
+ ClassNode cn = new ClassNode();
+ cr.accept(cn, 0);
+
+ boolean changed = false;
+
+ if ( this.verifyAPI(cn) )
+ changed = true;
+
+ if ( changed )
+ {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ cn.accept(cw);
+ bytes = cw.toByteArray();
+ log( "Altering definition of " + transformedName );
+ }
+ }
+
+ return bytes;
+ }
+
+ private boolean isInternal( String path )
+ {
+ return path.startsWith( "java/" )
+ || path.startsWith( "cpw/mods/fml/" )
+ || path.startsWith( "tconstruct/" )
+ || path.startsWith( "com/google/" )
+ || path.startsWith( "net/minecraftforge/" )
+ || path.startsWith( "net/minecraft/" );
+ }
+
+ private boolean isClassAvailable( String inf )
+ {
+ if ( APIInterfaces.containsKey( inf ) )
+ return APIInterfaces.get( inf );
+
+ boolean isAvailable = false;
+ Class obj = null;
+
+ try
+ {
+ obj = Class.forName( inf.replace( "/", "." ) );
+ //obj = ReflectionHelper.getClass( getClass().getClassLoader(), inf.replace( "/", "." ) );
+ }
+ catch ( Throwable _ ) {}
+
+ // java.io.InputStream is = this.getClass().getResourceAsStream( "/" + inf + ".class" );
+ APIInterfaces.put( inf, isAvailable = ( obj != null) );
+ return isAvailable;
+ }
+
+ class SigChecker extends SignatureVisitor {
+
+ private ASMInterfaceRepair asmTransformer;
+ public boolean isAvailable = true;
+
+ public SigChecker(int api, ASMInterfaceRepair t) {
+ super(api);
+ asmTransformer = t;
+ }
+
+ @Override
+ public void visitInnerClassType(String name) {
+ visitTypeVariable(name);
+ }
+
+ @Override
+ public void visitClassType(String name) {
+ visitTypeVariable(name);
+ }
+
+ @Override
+ public void visitTypeVariable(String className) {
+ if ( isInternal( className ) ) return;
+ isAvailable = isAvailable && asmTransformer.isClassAvailable( className );
+ log( className + " is " + ( isAvailable ? "available" : "not available" ) );
+ }
+
+ }
+
+ private boolean verifyAPI(ClassNode cn)
+ {
+ boolean changed = false;
+
+ //log( "Examining " + cn.name );
+
+ Iterator mn = cn.methods.iterator();
+ while ( mn.hasNext() )
+ {
+ MethodNode meth = mn.next();
+
+ SigChecker sc = new SigChecker(1,this);
+ ( new SignatureReader(meth.desc) ).accept( sc );
+
+ boolean isAvailable = sc.isAvailable;
+
+ if ( ! isAvailable )
+ {
+ mn.remove();
+ changed = true;
+ }
+ }
+
+ Iterator i = cn.interfaces.iterator();
+ while ( i.hasNext() )
+ {
+ String inf = i.next();
+
+ if ( isInternal( inf ) )
+ continue;
+
+ boolean isAvailable = isClassAvailable( inf );
+ log( inf + " is " + ( isAvailable ? "available" : "not available" ) );
+
+ if ( ! isAvailable )
+ {
+ i.remove();
+ changed = true;
+ }
+ }
+
+ return changed;
+ }
+
+ private void log(String string) {
+ //TConstructLoaderContainer.logger.info( string );
+ }
+
+}
diff --git a/src/tconstruct/preloader/TConstructLoaderContainer.java b/src/tconstruct/preloader/TConstructLoaderContainer.java
new file mode 100644
index 00000000..4c66d33c
--- /dev/null
+++ b/src/tconstruct/preloader/TConstructLoaderContainer.java
@@ -0,0 +1,110 @@
+package tconstruct.preloader;
+
+/*
+ * TConstructLoaderContainer is derived from code graciously provided by AlgorithmX2 of Applied Energistics.
+ * The code was adapted for this loader by Sunstrike. Any queries should be directed to him, not AlgorithmX2.
+ */
+
+import com.google.common.eventbus.EventBus;
+import cpw.mods.fml.common.DummyModContainer;
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.LoadController;
+import cpw.mods.fml.common.ModMetadata;
+import cpw.mods.fml.common.event.FMLInitializationEvent;
+import cpw.mods.fml.common.event.FMLPostInitializationEvent;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
+import net.minecraftforge.event.ForgeSubscribe;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public class TConstructLoaderContainer extends DummyModContainer implements IFMLLoadingPlugin {
+
+ private final String[] asmTransformers = new String[]{"tconstruct.preloader.ASMInterfaceRepair"};
+ private final ModMetadata md = new ModMetadata();
+
+ protected static Logger logger = Logger.getLogger("TCon-Preloader");
+
+ public TConstructLoaderContainer() {
+ logger.setParent(FMLLog.getLogger());
+ logger.info("Scalpel. Suction. Lumber axe. CLEAR! *zap*");
+ logger.info("Constructing preloader (Modules: " + Arrays.toString(asmTransformers) + ")");
+ md.autogenerated = true;
+ md.credits = "AlgorithmX2 (original) and Sunstrike (porting)";
+ md.modId = getModId();
+ md.version = getVersion();
+ md.name = getName();
+ md.authorList = Arrays.asList("AlgorithmX2", "Sunstrike");
+ }
+
+ @ForgeSubscribe
+ public void preInit(FMLPreInitializationEvent evt) {
+
+ }
+
+ @ForgeSubscribe
+ public void init(FMLInitializationEvent evt) {
+
+ }
+
+ @ForgeSubscribe
+ public void postInit(FMLPostInitializationEvent evt) {
+
+ }
+
+ @Override
+ public String getModId() {
+ return "TConstruct-Preloader";
+ }
+
+ @Override
+ public String getName() {
+ return "Tinkers Corestruct";
+ }
+
+ @Override
+ public String getVersion() {
+ return "0.0.1";
+ }
+
+ @Override
+ public String getDisplayVersion() {
+ return getVersion();
+ }
+
+ @Override
+ public ModMetadata getMetadata() {
+ return md;
+ }
+
+ @Override
+ public boolean registerBus(EventBus bus, LoadController controller) {
+ bus.register(this);
+ return true;
+ }
+
+ @Override
+ public String[] getLibraryRequestClass() {
+ return null;
+ }
+
+ @Override
+ public String[] getASMTransformerClass() {
+ return asmTransformers;
+ }
+
+ @Override
+ public String getModContainerClass() {
+ return "tconstruct.preloader.TConstructLoaderContainer";
+ }
+
+ @Override
+ public String getSetupClass() {
+ return null;
+ }
+
+ @Override
+ public void injectData(Map data) {}
+}