package appeng.core.api; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.client.MinecraftForgeClient; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.RemappingClassAdapter; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import appeng.api.parts.IPartHelper; import appeng.api.parts.IPartItem; import appeng.api.parts.LayerBase; import appeng.client.render.BusRenderer; import appeng.core.AELog; import appeng.core.AppEng; import appeng.helpers.PartPlacement; import appeng.integration.abstraction.IFMP; import appeng.tile.networking.TileCableBus; import appeng.util.Platform; import com.google.common.base.Joiner; public class ApiPart implements IPartHelper { int classNum = 1; HashMap TileImplementations = new HashMap(); HashMap readerCache = new HashMap(); HashMap interfaces2Layer = new HashMap(); HashMap roots = new HashMap(); List desc = new LinkedList(); public void initFMPSupport() { for (Class layerInterface : interfaces2Layer.keySet()) { if ( AppEng.instance.isIntegrationEnabled( "FMP" ) ) ((IFMP) AppEng.instance.getIntegration( "FMP" )).registerPassThru( layerInterface ); } } private Class loadClass(String Name, byte[] b) { // override classDefine (as it is protected) and define the class. Class clazz = null; try { ClassLoader loader = getClass().getClassLoader();// ClassLoader.getSystemClassLoader(); Class root = ClassLoader.class; Class cls = loader.getClass(); java.lang.reflect.Method defineClassMethod = root.getDeclaredMethod( "defineClass", new Class[] { String.class, byte[].class, int.class, int.class } ); java.lang.reflect.Method runTransformersMethod = cls .getDeclaredMethod( "runTransformers", new Class[] { String.class, String.class, byte[].class } ); runTransformersMethod.setAccessible( true ); defineClassMethod.setAccessible( true ); try { Object[] argsA = new Object[] { Name, Name, b }; b = (byte[]) runTransformersMethod.invoke( loader, argsA ); Object[] args = new Object[] { Name, b, new Integer( 0 ), new Integer( b.length ) }; clazz = (Class) defineClassMethod.invoke( loader, args ); } finally { runTransformersMethod.setAccessible( false ); defineClassMethod.setAccessible( false ); } } catch (Exception e) { AELog.error( e ); System.exit( 1 ); } return clazz; } public ClassNode getReader(String name) throws IOException { ClassReader cr; String path = "/" + name.replace( ".", "/" ) + ".class"; InputStream is = getClass().getResourceAsStream( path ); cr = new ClassReader( is ); ClassNode cn = new ClassNode(); cr.accept( cn, ClassReader.EXPAND_FRAMES ); return cn; } public Class getCombinedInstance(String base) { if ( desc.size() == 0 ) { try { return Class.forName( base ); } catch (Throwable t) { throw new RuntimeException( t ); } } String description = base + ":" + Joiner.on( ";" ).skipNulls().join( desc.iterator() ); if ( TileImplementations.get( description ) != null ) { try { return TileImplementations.get( description ); } catch (Throwable t) { throw new RuntimeException( t ); } } String f = base;// TileCableBus.class.getName(); String Addendum = ""; try { Addendum = Class.forName( base ).getSimpleName(); } catch (ClassNotFoundException e) { AELog.error( e ); } Class myCLass; try { myCLass = Class.forName( f ); } catch (Throwable t) { throw new RuntimeException( t ); } String path = f; for (String name : desc) { try { String newPath = path + ";" + name; myCLass = getClassByDesc( Addendum, newPath, f, interfaces2Layer.get( Class.forName( name ) ) ); path = newPath; } catch (Throwable t) { AELog.error( t ); // throw new RuntimeException( t ); } f = myCLass.getName(); } TileImplementations.put( description, myCLass ); try { return myCLass; } catch (Throwable t) { throw new RuntimeException( t ); } } class DefaultPackageClassNameRemapper extends Remapper { public HashMap inputOutput = new HashMap(); @Override public String map(String typeName) { String o = inputOutput.get( typeName ); if ( o == null ) return typeName; return o; } } public Class getClassByDesc(String Addendum, String fullPath, String root, String next) throws IOException { if ( roots.get( fullPath ) != null ) return roots.get( fullPath ); ClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS ); ClassNode n = getReader( next ); String originalName = n.name; try { n.name = n.name + "_" + Addendum; n.superName = Class.forName( root ).getName().replace( ".", "/" ); } catch (Throwable t) { AELog.error( t ); } for (MethodNode mn : n.methods) { Iterator i = mn.instructions.iterator(); while (i.hasNext()) { processNode( i.next(), n.superName ); } } DefaultPackageClassNameRemapper remapper = new DefaultPackageClassNameRemapper(); remapper.inputOutput.put( "appeng/api/parts/LayerBase", n.superName ); remapper.inputOutput.put( originalName, n.name ); n.accept( new RemappingClassAdapter( cw, remapper ) ); // n.accept( cw ); // n.accept( new TraceClassVisitor( new PrintWriter( System.out ) ) ); byte[] barray = cw.toByteArray(); int size = barray.length; Class nclass = loadClass( n.name.replace( "/", "." ), barray ); try { Object fish = nclass.newInstance(); Class rootC = Class.forName( root ); boolean bads = false; if ( !rootC.isInstance( fish ) ) { bads = true; AELog.severe( "Error, Expected layer to implement " + root + " did not." ); } if ( fish instanceof LayerBase ) { bads = true; AELog.severe( "Error, Expected layer to NOT implement LayerBase but it DID." ); } if ( !fullPath.contains( ".fmp." ) ) { if ( !(fish instanceof TileCableBus) ) { bads = true; AELog.severe( "Error, Expected layer to implement TileCableBus did not." ); } if ( !(fish instanceof TileEntity) ) { bads = true; AELog.severe( "Error, Expected layer to implement TileEntity did not." ); } } if ( !bads ) { AELog.info( "Layer: " + n.name + " loaded successfully - " + size + " bytes" ); } } catch (Throwable t) { AELog.severe( "Layer: " + n.name + " Failed." ); AELog.error( t ); } roots.put( fullPath, nclass ); return nclass; } private void processNode(AbstractInsnNode next, String nePar) { if ( next instanceof MethodInsnNode ) { MethodInsnNode min = (MethodInsnNode) next; if ( min.owner.equals( "appeng/api/parts/LayerBase" ) ) { min.owner = nePar; } } } @Override public void setItemBusRenderer(IPartItem i) { if ( Platform.isClient() && i instanceof Item ) MinecraftForgeClient.registerItemRenderer( (Item) i, BusRenderer.instance ); } @Override public boolean placeBus(ItemStack is, int x, int y, int z, int side, EntityPlayer player, World w) { return PartPlacement.place( is, x, y, z, side, player, w, PartPlacement.PlaceType.PLACE_ITEM, 0 ); } @Override public boolean registerNewLayer(String layer, String layerInterface) { try { if ( interfaces2Layer.get( layerInterface ) == null ) { interfaces2Layer.put( Class.forName( layerInterface ), layer ); desc.add( layerInterface ); return true; } else AELog.info( "Layer " + layer + " not registered, " + layerInterface + " aready has a layer." ); } catch (Throwable t) { } return false; } }