diff --git a/src/codechicken/microblock/CornerMicroblock.scala b/src/codechicken/microblock/CornerMicroblock.scala index 3438780..7b8e413 100644 --- a/src/codechicken/microblock/CornerMicroblock.scala +++ b/src/codechicken/microblock/CornerMicroblock.scala @@ -14,7 +14,7 @@ object CornerPlacement extends PlacementProperties def opposite(slot:Int, side:Int) = ((slot-7)^(1<<(side>>1)))+7 } -object CornerMicroClass extends MicroblockClass +object CornerMicroClass extends CommonMicroClass { var aBounds:Array[Cuboid6] = new Array(256) @@ -32,22 +32,19 @@ object CornerMicroClass extends MicroblockClass } } - override def itemSlot = 7 - def getName = "mcr_cnr" + + def baseTrait = classOf[CornerMicroblock] + def clientTrait = classOf[CommonMicroblockClient] - def create(client:Boolean, material:Int) = - if(client) - new CornerMicroblock(material)with CommonMicroblockClient - else - new CornerMicroblock(material) + override def itemSlot = 7 def placementProperties = CornerPlacement def getResistanceFactor = 1 } -class CornerMicroblock(material$:Int = 0) extends CommonMicroblock(material$) +trait CornerMicroblock extends CommonMicroblock { override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-7)).toByte diff --git a/src/codechicken/microblock/EdgeMicroblock.scala b/src/codechicken/microblock/EdgeMicroblock.scala index 05d07a5..666ee94 100644 --- a/src/codechicken/microblock/EdgeMicroblock.scala +++ b/src/codechicken/microblock/EdgeMicroblock.scala @@ -66,7 +66,7 @@ object EdgePlacement extends PlacementProperties } } -object EdgeMicroClass extends MicroblockClass +object EdgeMicroClass extends CommonMicroClass { var aBounds:Array[Cuboid6] = new Array(256) @@ -86,19 +86,16 @@ object EdgeMicroClass extends MicroblockClass override def itemSlot = 15 def getName = "mcr_edge" - - def create(client:Boolean, material:Int) = - if(client) - new EdgeMicroblock(material) with CommonMicroblockClient - else - new EdgeMicroblock(material) - + + def baseTrait = classOf[EdgeMicroblock] + def clientTrait = classOf[CommonMicroblockClient] + def placementProperties = EdgePlacement def getResistanceFactor = 0.5F } -class EdgeMicroblock(material$:Int = 0) extends CommonMicroblock(material$) with TEdgePart +trait EdgeMicroblock extends CommonMicroblock with TEdgePart { override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-15)).toByte @@ -109,7 +106,7 @@ class EdgeMicroblock(material$:Int = 0) extends CommonMicroblock(material$) with override def getSlot = getShape+15 } -object PostMicroClass +object PostMicroClass extends MicroblockClass { var aBounds:Array[Cuboid6] = new Array(256) @@ -125,14 +122,9 @@ object PostMicroClass } def getName = "mcr_post" - - def create(client:Boolean, material:Int) = - if(client) - new PostMicroblock(material) with PostMicroblockClient - else - new PostMicroblock(material) - - def register() = MultiPartRegistry.registerParts((_, c:Boolean) => create(c, 0), getName) + + def baseTrait = classOf[PostMicroblock] + def clientTrait = classOf[PostMicroblockClient] def getResistanceFactor = 0.5F } @@ -214,9 +206,9 @@ trait PostMicroblockClient extends PostMicroblock with MicroblockClient } } -class PostMicroblock(material$:Int = 0) extends Microblock(material$) with JPartialOcclusion with TNormalOcclusion +trait PostMicroblock extends Microblock with JPartialOcclusion with TNormalOcclusion { - override def getType = PostMicroClass.getName + def microClass = PostMicroClass def getBounds = PostMicroClass.aBounds(shape) @@ -224,7 +216,7 @@ class PostMicroblock(material$:Int = 0) extends Microblock(material$) with JPart def getPartialOcclusionBoxes = getOcclusionBoxes - override def itemClassID = EdgeMicroClass.classID + override def itemClassID = EdgeMicroClass.getClassId override def occlusionTest(npart:TMultiPart): Boolean = { diff --git a/src/codechicken/microblock/FaceMicroblock.scala b/src/codechicken/microblock/FaceMicroblock.scala index 17fa549..788ecf7 100644 --- a/src/codechicken/microblock/FaceMicroblock.scala +++ b/src/codechicken/microblock/FaceMicroblock.scala @@ -22,7 +22,9 @@ object FacePlacement extends PlacementProperties override def sneakOpposite(slot:Int, side:Int) = slot == (side^1) } -object FaceMicroClass extends MicroblockClass +class FaceMicroClass + +object FaceMicroClass extends CommonMicroClass { var aBounds:Array[Cuboid6] = new Array(256) @@ -37,12 +39,11 @@ object FaceMicroClass extends MicroblockClass } def getName = "mcr_face" - - def create(client:Boolean, material:Int) = - if(client) - new FaceMicroblock(material) with FaceMicroblockClient - else - new FaceMicroblock(material) + + def itemSlot = 3 + + def baseTrait = classOf[FaceMicroblock] + def clientTrait = classOf[FaceMicroblockClient] def placementProperties = FacePlacement @@ -64,7 +65,7 @@ trait FaceMicroblockClient extends CommonMicroblockClient } } -class FaceMicroblock(material$:Int = 0) extends CommonMicroblock(material$) with TFacePart +trait FaceMicroblock extends CommonMicroblock with TFacePart { def microClass = FaceMicroClass diff --git a/src/codechicken/microblock/HollowMicroblock.scala b/src/codechicken/microblock/HollowMicroblock.scala index 8ec3eb7..16e4276 100644 --- a/src/codechicken/microblock/HollowMicroblock.scala +++ b/src/codechicken/microblock/HollowMicroblock.scala @@ -18,7 +18,6 @@ import codechicken.lib.lighting.LightMatrix object HollowPlacement extends PlacementProperties { - object HollowPlacementGrid extends FaceEdgeGrid(3 / 8D) def microClass = HollowMicroClass @@ -32,7 +31,7 @@ object HollowPlacement extends PlacementProperties override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1) } -object HollowMicroClass extends MicroblockClass +object HollowMicroClass extends CommonMicroClass { var pBoxes: Array[Seq[Cuboid6]] = new Array(256) var occBounds: Array[Cuboid6] = new Array(256) @@ -54,11 +53,10 @@ object HollowMicroClass extends MicroblockClass def getName = "mcr_hllw" - def create(client:Boolean, material:Int) = - if(client) - new HollowMicroblock(material) with HollowMicroblockClient - else - new HollowMicroblock(material) + def baseTrait = classOf[HollowMicroblock] + def clientTrait = classOf[HollowMicroblockClient] + + def itemSlot = 3 def placementProperties = HollowPlacement @@ -172,7 +170,7 @@ trait HollowMicroblockClient extends HollowMicroblock with CommonMicroblockClien } } -class HollowMicroblock(material$: Int = 0) extends CommonMicroblock(material$) with TFacePart with TNormalOcclusion +trait HollowMicroblock extends CommonMicroblock with TFacePart with TNormalOcclusion { def microClass = HollowMicroClass diff --git a/src/codechicken/microblock/ItemMicroPart.scala b/src/codechicken/microblock/ItemMicroPart.scala index b47a236..2522628 100644 --- a/src/codechicken/microblock/ItemMicroPart.scala +++ b/src/codechicken/microblock/ItemMicroPart.scala @@ -11,7 +11,6 @@ import org.lwjgl.opengl.GL11 import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial import java.util.List import net.minecraft.creativetab.CreativeTabs -import codechicken.microblock.MicroblockClassRegistry._ import codechicken.lib.render.CCRenderState import net.minecraft.client.renderer.texture.IIconRegister import codechicken.microblock.handler.MicroblockProxy @@ -22,6 +21,7 @@ import codechicken.lib.raytracer.RayTracer import codechicken.lib.vec.Vector3 import codechicken.lib.render.TextureUtils import net.minecraft.util.MovingObjectPosition.MovingObjectType +import CommonMicroClass._ class ItemMicroPart extends Item { @@ -31,7 +31,7 @@ class ItemMicroPart extends Item override def getItemStackDisplayName(stack:ItemStack):String = { val material = getMaterial(stack) - val mcrClass = MicroblockClassRegistry.getMicroClass(stack.getItemDamage) + val mcrClass = getMicroClass(stack.getItemDamage) val size = stack.getItemDamage&0xFF if(material == null || mcrClass == null) return "Unnamed" @@ -56,7 +56,7 @@ class ItemMicroPart extends Item override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, s:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean = { val material = getMaterialID(item) - val mcrClass = MicroblockClassRegistry.getMicroClass(item.getItemDamage) + val mcrClass = getMicroClass(item.getItemDamage) val size = item.getItemDamage&0xFF if(material < 0 || mcrClass == null) return false @@ -134,7 +134,7 @@ object ItemMicroPartRenderer extends IItemRenderer def renderItem(t:ItemRenderType, item:ItemStack, data:Object*) { val material = getMaterial(item) - val mcrClass = MicroblockClassRegistry.getMicroClass(item.getItemDamage) + val mcrClass = getMicroClass(item.getItemDamage) val size = item.getItemDamage&0xFF if(material == null || mcrClass == null) return @@ -160,7 +160,7 @@ object ItemMicroPartRenderer extends IItemRenderer def renderHighlight(player:EntityPlayer, stack:ItemStack, hit:MovingObjectPosition):Boolean = { val material = getMaterialID(stack) - val mcrClass = MicroblockClassRegistry.getMicroClass(stack.getItemDamage) + val mcrClass = getMicroClass(stack.getItemDamage) val size = stack.getItemDamage&0xFF if(material < 0 || mcrClass == null) return false diff --git a/src/codechicken/microblock/MicroMaterialRegistry.scala b/src/codechicken/microblock/MicroMaterialRegistry.scala index 680d1b5..949052d 100644 --- a/src/codechicken/microblock/MicroMaterialRegistry.scala +++ b/src/codechicken/microblock/MicroMaterialRegistry.scala @@ -107,7 +107,7 @@ object MicroMaterialRegistry /** * Return true if a custom highlight was rendered and the default should be skipped */ - def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: MicroblockClass, size: Int, material: Int): Boolean + def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: CommonMicroClass, size: Int, material: Int): Boolean } private val typeMap = HashMap[String, IMicroMaterial]() @@ -229,7 +229,7 @@ object MicroMaterialRegistry def getIdMap = idMap - def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: MicroblockClass, size: Int, material: Int): Boolean = { + def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: CommonMicroClass, size: Int, material: Int): Boolean = { val overridden = highlightRenderers.find(_.renderHighlight(player, hit, mcrClass, size, material)) if (overridden.isDefined) return true diff --git a/src/codechicken/microblock/CommonMicroblock.scala b/src/codechicken/microblock/Microblock.scala similarity index 64% rename from src/codechicken/microblock/CommonMicroblock.scala rename to src/codechicken/microblock/Microblock.scala index 23f1974..2682d0f 100644 --- a/src/codechicken/microblock/CommonMicroblock.scala +++ b/src/codechicken/microblock/Microblock.scala @@ -31,48 +31,88 @@ import net.minecraft.init.Blocks import cpw.mods.fml.relauncher.{Side, SideOnly} import codechicken.lib.render.BlockRenderer.BlockFace -object MicroblockRender +abstract class Microblock(var material:Int = 0) extends TCuboidPart { - def renderHighlight(player:EntityPlayer, hit:MovingObjectPosition, mcrClass:MicroblockClass, size:Int, material:Int) - { - mcrClass.placementProperties.placementGrid.render(new Vector3(hit.hitVec), hit.sideHit) + var shape:Byte = 0 - val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties) - if(placement == null) - return - val pos = placement.pos - val part = placement.part.asInstanceOf[MicroblockClient] + def microClass:MicroblockClass - GL11.glPushMatrix() - GL11.glTranslated(pos.x+0.5, pos.y+0.5, pos.z+0.5) - GL11.glScaled(1.002, 1.002, 1.002) - GL11.glTranslated(-0.5, -0.5, -0.5) - - GL11.glEnable(GL11.GL_BLEND) - GL11.glDepthMask(false) - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) + def getType = microClass.getName - TextureUtils.bindAtlas(0) - CCRenderState.reset() - CCRenderState.alphaOverride = 80 - CCRenderState.useNormals = true - CCRenderState.startDrawing() - part.render(Vector3.zero, -1) - CCRenderState.draw() - - GL11.glDisable(GL11.GL_BLEND) - GL11.glDepthMask(true) - GL11.glPopMatrix() + override def getStrength(hit:MovingObjectPosition, player:EntityPlayer) = getIMaterial match { + case null => super.getStrength(hit, player) + case mat => mat.getStrength(player) } - val face = new BlockFace() - def renderCuboid(pos:Vector3, mat:IMicroMaterial, pass:Int, c:Cuboid6, faces:Int) { - CCRenderState.setModel(face) - for(s <- 0 until 6 if (faces & 1<>4 + + def getShape = shape&0xF + + def setShape(size:Int, slot:Int) = shape = (size<<4|slot).toByte + + def getMaterial = material + + def getIMaterial = MicroMaterialRegistry.getMaterial(material) + + def itemClassID:Int + + override def getDrops = { + var size = getSize + val items = ListBuffer[ItemStack]() + for(s <- Seq(4, 2, 1)) + { + val m = size/s + size-=m*s + if(m > 0) + items+=ItemMicroPart.create(m, s|itemClassID<<8, MicroMaterialRegistry.materialName(material)) } + items } + + override def pickItem(hit:MovingObjectPosition):ItemStack = { + val size = getSize + for(s <- Seq(4, 2, 1)) + if(size%s == 0 && size/s >= 1) + return ItemMicroPart.create(s|itemClassID<<8, MicroMaterialRegistry.materialName(material)) + + return null//unreachable + } + + override def writeDesc(packet:MCDataOutput) { + writeMaterialID(packet, material) + packet.writeByte(shape) + } + + override def readDesc(packet:MCDataInput) { + shape = packet.readByte + } + + def sendShapeUpdate() { + getWriteStream.writeByte(shape) + } + + override def read(packet:MCDataInput) { + super.read(packet) + tile.notifyPartChange(this) + } + + override def save(tag:NBTTagCompound) { + tag.setByte("shape", shape) + tag.setString("material", materialName(material)) + } + + override def load(tag:NBTTagCompound) { + shape = tag.getByte("shape") + material = materialID(tag.getString("material")) + } + + def isTransparent = getIMaterial.isTransparent + + override def getLightValue = getIMaterial.getLightValue + + override def explosionResistance(entity:Entity) = getIMaterial.explosionResistance(entity) * microClass.getResistanceFactor } trait MicroblockClient extends Microblock with TIconHitEffects with IMicroMaterialRender @@ -96,92 +136,6 @@ trait MicroblockClient extends Microblock with TIconHitEffects with IMicroMateri override def getRenderBounds = getBounds } -abstract class Microblock(var material:Int = 0) extends TCuboidPart -{ - var shape:Byte = 0 - - override def getStrength(hit:MovingObjectPosition, player:EntityPlayer) = getIMaterial match { - case null => super.getStrength(hit, player) - case mat => mat.getStrength(player) - } - - override def doesTick = false - - def getSize = shape>>4 - - def getShape = shape&0xF - - def setShape(size:Int, slot:Int) = shape = (size<<4|slot).toByte - - def getMaterial = material - - def getIMaterial = MicroMaterialRegistry.getMaterial(material) - - def itemClassID:Int = -1 - - override def getDrops = - { - var size = getSize - val items = ListBuffer[ItemStack]() - for(s <- Seq(4, 2, 1)) - { - val m = size/s - size-=m*s - if(m > 0) - items+=ItemMicroPart.create(m, s|itemClassID<<8, MicroMaterialRegistry.materialName(material)) - } - items - } - - override def pickItem(hit:MovingObjectPosition):ItemStack = - { - val size = getSize - for(s <- Seq(4, 2, 1)) - if(size%s == 0 && size/s >= 1) - return ItemMicroPart.create(s|itemClassID<<8, MicroMaterialRegistry.materialName(material)) - return null//unreachable - } - - override def writeDesc(packet:MCDataOutput) - { - packet.writeByte(shape) - writeMaterialID(packet, material) - } - - override def readDesc(packet:MCDataInput) - { - shape = packet.readByte - material = readMaterialID(packet) - } - - override def read(packet:MCDataInput) - { - readDesc(packet) - tile.notifyPartChange(this) - tile.markRender() - } - - override def save(tag:NBTTagCompound) - { - tag.setByte("shape", shape) - tag.setString("material", materialName(material)) - } - - override def load(tag:NBTTagCompound) - { - shape = tag.getByte("shape") - material = materialID(tag.getString("material")) - } - - def isTransparent = getIMaterial.isTransparent - - override def getLightValue = getIMaterial.getLightValue - - def getResistanceFactor:Float - - override def explosionResistance(entity:Entity) = getIMaterial.explosionResistance(entity)*getResistanceFactor -} - trait CommonMicroblockClient extends CommonMicroblock with MicroblockClient with TMicroOcclusionClient { def render(pos:Vector3, pass:Int) { @@ -192,19 +146,13 @@ trait CommonMicroblockClient extends CommonMicroblock with MicroblockClient with } } -abstract class CommonMicroblock(material$:Int) extends Microblock(material$) with JPartialOcclusion with TMicroOcclusion with TSlottedPart +trait CommonMicroblock extends Microblock with JPartialOcclusion with TMicroOcclusion with TSlottedPart { - def microClass:MicroblockClass - - def getType = microClass.getName + def microClass:CommonMicroClass def getSlot = getShape - def getSlotMask = 1<>8) + + def registerMicroClass(mcrClass:CommonMicroClass, id:Int) { + if(classes(id) != null) + throw new IllegalArgumentException("Microblock class id "+id+" is already taken by "+classes(id).getName+" when adding "+mcrClass.getName) + + classes(id) = mcrClass + } } \ No newline at end of file diff --git a/src/codechicken/microblock/MicroblockClassRegistry.scala b/src/codechicken/microblock/MicroblockClassRegistry.scala deleted file mode 100644 index 7f9f978..0000000 --- a/src/codechicken/microblock/MicroblockClassRegistry.scala +++ /dev/null @@ -1,20 +0,0 @@ -package codechicken.microblock - -import codechicken.multipart.MultiPartRegistry - -object MicroblockClassRegistry -{ - val classes = new Array[MicroblockClass](256) - - def getMicroClass(modelId:Int) = classes(modelId>>8) - - def registerMicroClass(mcrClass:MicroblockClass, id:Int) - { - if(classes(id) != null) - throw new IllegalArgumentException("Microblock class id "+id+" is already taken by "+classes(id).getName+" when adding "+mcrClass.getName) - - classes(id) = mcrClass - mcrClass.classID = id - MultiPartRegistry.registerParts((_, c:Boolean) => mcrClass.create(c, 0), mcrClass.getName) - } -} \ No newline at end of file diff --git a/src/codechicken/microblock/MicroblockGenerator.scala b/src/codechicken/microblock/MicroblockGenerator.scala new file mode 100644 index 0000000..20bb285 --- /dev/null +++ b/src/codechicken/microblock/MicroblockGenerator.scala @@ -0,0 +1,28 @@ +package codechicken.microblock + +import codechicken.multipart.asm.ASMMixinFactory +import java.util.BitSet + +object MicroblockGenerator extends ASMMixinFactory(classOf[Microblock], classOf[Int]) +{ + trait IGeneratedMaterial + { + def addTraits(traits:BitSet, mcrClass:MicroblockClass, client:Boolean) + } + + //scratch bitset + private val bitset = new BitSet + + def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = { + bitset.clear() + bitset.set(mcrClass.baseTraitId) + if(client) bitset.set(mcrClass.clientTraitId) + + MicroMaterialRegistry.getMaterial(material) match { + case genMat:IGeneratedMaterial => genMat.addTraits(bitset, mcrClass, client) + case _ => + } + + construct(bitset, material:Integer) + } +} diff --git a/src/codechicken/microblock/MicroblockPlacement.scala b/src/codechicken/microblock/MicroblockPlacement.scala index 303faba..e41d5b8 100644 --- a/src/codechicken/microblock/MicroblockPlacement.scala +++ b/src/codechicken/microblock/MicroblockPlacement.scala @@ -36,7 +36,7 @@ class ExpandingPlacement($pos:BlockCoord, $part:Microblock, opart:Microblock) ex { opart.shape = part.shape opart.tile.notifyPartChange(opart) - opart.sendDescUpdate() + opart.sendShapeUpdate() } def consume(world:World, player:EntityPlayer, item:ItemStack) diff --git a/src/codechicken/microblock/MicroblockRender.scala b/src/codechicken/microblock/MicroblockRender.scala new file mode 100644 index 0000000..27c149e --- /dev/null +++ b/src/codechicken/microblock/MicroblockRender.scala @@ -0,0 +1,55 @@ +package codechicken.microblock + +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.util.MovingObjectPosition +import codechicken.lib.vec.{Cuboid6, Vector3} +import org.lwjgl.opengl.GL11._ +import codechicken.lib.render.{CCRenderState, TextureUtils} +import codechicken.lib.render.BlockRenderer.BlockFace +import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial + +object MicroblockRender +{ + def renderHighlight(player:EntityPlayer, hit:MovingObjectPosition, mcrClass:CommonMicroClass, size:Int, material:Int) + { + mcrClass.placementProperties.placementGrid.render(new Vector3(hit.hitVec), hit.sideHit) + + val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties) + if(placement == null) + return + val pos = placement.pos + val part = placement.part.asInstanceOf[MicroblockClient] + + glPushMatrix() + glTranslated(pos.x+0.5, pos.y+0.5, pos.z+0.5) + glScaled(1.002, 1.002, 1.002) + glTranslated(-0.5, -0.5, -0.5) + + glEnable(GL_BLEND) + glDepthMask(false) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + TextureUtils.bindAtlas(0) + CCRenderState.reset() + CCRenderState.alphaOverride = 80 + CCRenderState.useNormals = true + CCRenderState.startDrawing() + part.render(Vector3.zero, -1) + CCRenderState.draw() + + glDisable(GL_BLEND) + glDepthMask(true) + glPopMatrix() + } + + val face = new BlockFace() + def renderCuboid(pos:Vector3, mat:IMicroMaterial, pass:Int, c:Cuboid6, faces:Int) { + CCRenderState.setModel(face) + for(s <- 0 until 6 if (faces & 1< m.name == name && m.desc == desc) - def findMethod(name:String, desc:String, f:(MethodInfo)=>Boolean):Option[MethodInfo] = - getMethod(name, desc).filter(f) orElse (superClass ++ interfaces).view.flatMap(_.findMethod(name, desc, f)).headOption - def findPublicImpl(name:String, desc:String) = findMethod(name, desc, m => !m.isAbstract && !m.isPrivate) + def parentMethods = (superClass ++ interfaces).view.flatMap(_.allMethods) + def allMethods:Iterable[MethodInfo] = methods ++ parentMethods + def findPublicImpl(name:String, desc:String) = allMethods.find(m => m.name == name && m.desc == desc && !m.isAbstract && !m.isPrivate) + + def isScala = false + def isTrait = false + def isObject = false + def moduleName = name } private val infoCache = MMap[String, ClassInfo]() @@ -174,6 +169,7 @@ object ASMMixinCompiler def owner = ReflectionClassInfo.this def name = method.getName def desc = getType(method).getDescriptor + def exceptions = method.getExceptionTypes.map(_.nodeName) def isPrivate = Modifier.isPrivate(method.getModifiers) def isAbstract = Modifier.isAbstract(method.getModifiers) } @@ -184,13 +180,14 @@ object ASMMixinCompiler def methods = clazz.getMethods.map(ReflectionMethodInfo(_)) } - class ClassNodeInfo(cnode: ClassNode) extends ClassInfo + class ClassNodeInfo(val cnode: ClassNode) extends ClassInfo { case class MethodNodeInfoSource(mnode: MethodNode) extends MethodInfo { def owner = ClassNodeInfo.this def name = mnode.name def desc = mnode.desc + def exceptions = Array(mnode.exceptions:_*) def isPrivate = (mnode.access & ACC_PRIVATE) != 0 def isAbstract = (mnode.access & ACC_ABSTRACT) != 0 } @@ -201,13 +198,16 @@ object ASMMixinCompiler def methods = cnode.methods.map(MethodNodeInfoSource) } - class ScalaClassInfo(cnode: ClassNode) extends ClassNodeInfo(cnode) + class ScalaClassInfo(cnode$: ClassNode) extends ClassNodeInfo(cnode$) { val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get) val csym = sig.evalT(0): ClassSymbol override def superClass = Some(csym.jParent(sig)) override def interfaces = csym.jInterfaces(sig).map(getClassInfo) + override def isScala = true + + override def isTrait = csym.isTrait } private[ASMMixinCompiler] def obtainInfo(name: String): ClassInfo = name match { @@ -217,12 +217,14 @@ object ASMMixinCompiler case null => null case c => new ReflectionClassInfo(c) } - case v if isScala(v) => new ScalaClassInfo(v) + case v if ScalaSigReader.ann(v).isDefined => new ScalaClassInfo(v) case v => new ClassNodeInfo(v) } } } + import StackAnalyser.width + def finishBridgeCall(mv: MethodVisitor, mvdesc: String, opcode: Int, owner: String, name: String, desc: String) { val args = getArgumentTypes(mvdesc) val ret = getReturnType(mvdesc) @@ -249,20 +251,22 @@ object ASMMixinCompiler val startTime = System.currentTimeMillis val baseTraits = traits.map(mixinMap) - val traitInfos = baseTraits.flatMap(_.linearise).distinct - val parentInfo = getClassInfo(superClass) + val mixinInfos = baseTraits.flatMap(_.linearise).distinct + val baseInfo = getClassInfo(superClass) + val traitInfos = mixinInfos.map(i => getClassInfo(i.name)) val cnode = new ClassNode() //implements list cnode.visit(V1_6, ACC_PUBLIC, name, null, superClass, baseTraits.map(_.name).toArray[String]) - val minit = cnode.visitMethod(ACC_PUBLIC, "", "()V", null, null) - minit.visitVarInsn(ALOAD, 0) - minit.visitMethodInsn(INVOKESPECIAL, cnode.superName, "", "()V", false) + val cinit = baseInfo.methods.find(_.name == "").get + val minit = cnode.visitMethod(ACC_PUBLIC, "", cinit.desc, null, null).asInstanceOf[MethodNode] + writeBridge(minit, cinit.desc, INVOKESPECIAL, superClass, "", cinit.desc) + minit.instructions.remove(minit.instructions.getLast)//remove the RETURN from writeBridge val prevInfos = MList[MixinInfo]() - traitInfos.foreach { t => + mixinInfos.foreach { t => minit.visitVarInsn(ALOAD, 0) minit.visitMethodInsn(INVOKESTATIC, t.tname, "$init$", "(L" + t.name + ";)V", false) @@ -288,10 +292,10 @@ object ASMMixinCompiler val (name, desc) = seperateDesc(s) val mv = cnode.visitMethod(ACC_PUBLIC, t.name.replace('/', '$') + "$$super$" + name, desc, null, null).asInstanceOf[MethodNode] - prevInfos.reverse.find(t => t.supers.contains(s)) match { + prevInfos.reverse.find(_.methods.exists(m => m.name == name && m.desc == desc)) match { //each super goes to the one before case Some(st) => writeStaticBridge(mv, name, st) - case None => writeBridge(mv, desc, INVOKESPECIAL, parentInfo.findPublicImpl(name, desc).get.owner.name, name, desc) + case None => writeBridge(mv, desc, INVOKESPECIAL, baseInfo.findPublicImpl(name, desc).get.owner.name, name, desc) } } @@ -299,10 +303,10 @@ object ASMMixinCompiler } val methodSigs = MSet[String]() - traitInfos.reverse.foreach { t => //last trait gets first pick on methods + mixinInfos.reverse.foreach { t => //last trait gets first pick on methods t.methods.foreach { m => if (!methodSigs(m.name + m.desc)) { - val mv = cnode.visitMethod(ACC_PUBLIC, m.name, m.desc, null, Array(m.exceptions: _*)).asInstanceOf[MethodNode] + val mv = cnode.visitMethod(ACC_PUBLIC, m.name, m.desc, null, Array(m.exceptions:_*)).asInstanceOf[MethodNode] writeStaticBridge(mv, m.name, t) methodSigs += m.name + m.desc } @@ -310,11 +314,27 @@ object ASMMixinCompiler } minit.visitInsn(RETURN) - minit.visitMaxs(1, 1) - val c = define(cnode.name, createBytes(cnode, 0)) + //generate synthetic bridge methods for covariant return types + def allParents(info:ClassInfo):Iterable[ClassInfo] = info +: (info.superClass ++ info.interfaces).toSeq.flatMap(allParents) + val allParentInfos = (baseInfo +: traitInfos).flatMap(allParents).distinct + val allParentMethods = allParentInfos.flatMap(_.methods) + methodSigs.seq.foreach { nameDesc => + val (name, desc) = seperateDesc(nameDesc) + val pDesc = desc.substring(0, desc.lastIndexOf(')')+1) - DebugPrinter.logger.debug("Generation ["+superClass+" with "+traits.mkString(", ")+"] took: "+(System.currentTimeMillis-startTime)) + allParentMethods.filter(m => m.name == name && m.desc.startsWith(pDesc)).foreach { m => + if(!methodSigs(m.name+m.desc)) { + val mv = cnode.visitMethod(ACC_PUBLIC | ACC_SYNTHETIC | ACC_BRIDGE, m.name, m.desc, null, m.exceptions).asInstanceOf[MethodNode] + writeBridge(mv, mv.desc, INVOKEVIRTUAL, cnode.name, name, desc) + methodSigs += m.name + m.desc + } + } + } + + val c = define(name, createBytes(cnode, 0)) + + DebugPrinter.logger.debug("Generation ["+superClass+" with "+traits.mkString(", ")+"] took "+(System.currentTimeMillis-startTime)+"ms") c } @@ -347,7 +367,10 @@ object ASMMixinCompiler getClassInfo(stack.owner.getInternalName).superClass.flatMap(_.findPublicImpl(methodName, minsn.desc)) } - def getAndRegisterParentTraits(cnode: ClassNode) = cnode.interfaces.map(classNode).filter(i => isScala(i) && isTrait(i)).map(registerScalaTrait) + def getAndRegisterParentTraits(cnode: ClassNode) = cnode.interfaces.map(getClassInfo).collect{ + case i: ClassInfo.ScalaClassInfo if i.isTrait && !i.csym.isInterface => + registerScalaTrait(i.cnode) + } def registerJavaTrait(cnode: ClassNode) { if ((cnode.access & ACC_INTERFACE) != 0) @@ -427,7 +450,7 @@ object ASMMixinCompiler if (getSuper(minsn, stack).isDefined) replace(superInsn(minsn)) case INVOKEVIRTUAL => - if (minsn.owner.equals(cnode.name)) { + if (minsn.owner == cnode.name) { if (methodSigs.contains(minsn.name + minsn.desc)) {//call the interface method replace(new MethodInsnNode(INVOKEINTERFACE, minsn.owner, minsn.name, minsn.desc, true)) } else { @@ -448,11 +471,11 @@ object ASMMixinCompiler } def convertMethod(mnode: MethodNode) { - if (mnode.name.equals("")) + if (mnode.name == "") throw new IllegalArgumentException("Static initialisers are not permitted " + cnode.name + " as a mixin trait") - if (mnode.name.equals("")) { - if (!mnode.desc.equals("()V")) + if (mnode.name == "") { + if (mnode.desc != "()V") throw new IllegalArgumentException("Constructor arguments are not permitted " + cnode.name + " as a mixin trait") val mv = staticClone(mnode, "$init$", ACC_PUBLIC) @@ -504,30 +527,29 @@ object ASMMixinCompiler val methods = MList[MethodNode]() val supers = MList[String]() - val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get) - val csym: ClassSymbol = sig.evalT(0) + val info = getClassInfo(cnode).asInstanceOf[ClassInfo.ScalaClassInfo] + val sig = info.sig + val csym = info.csym for (i <- 0 until sig.table.length) { import ScalaSignature._ val e = sig.table(i) if (e.id == 8) {//method val sym: MethodSymbol = sig.evalT(i) - if (sym.isParam || !sym.owner.equals(csym)) {} + if (sym.isParam || sym.owner != csym) {} else if (sym.isAccessor) { fieldAccessors.put(sym.name, sym) } else if (sym.isMethod) { val desc = sym.jDesc(sig) - if (sym.name.contains("super$")) { + if (sym.name.startsWith("super$")) supers += sym.name.substring(6) + desc - } - else if (!sym.name.equals("$init$") && !sym.isPrivate) { - val map = new ObfMapping(cnode.name, sym.name, desc) - val method = findMethod(map, cnode) - if (method == null) - throw new RuntimeException("Unable to add mixin trait: " + map + " found in scala signature but not in class file. Most likely an obfuscation issue.") - methods += method - } + else if (!sym.isPrivate && !sym.isDeferred && sym.name != "$init$") + methods += (cnode.methods.find(m => m.name == sym.name && m.desc == desc) match { + case Some(m) => m + case None => throw new IllegalArgumentException("Unable to add mixin trait "+cnode.name+": " + + sym.name + desc + " found in scala signature but not in class file. Most likely an obfuscation issue.") + }) } else { fields += FieldMixin(sym.name.trim, getReturnType(sym.jDesc(sig)).getDescriptor, @@ -536,8 +558,8 @@ object ASMMixinCompiler } } - val info = MixinInfo(cnode.name, csym.jParent(sig), parentTraits, fields, methods, supers) - mixinMap.put(cnode.name, info) - info + val mixin = MixinInfo(cnode.name, csym.jParent(sig), parentTraits, fields, methods, supers) + mixinMap.put(cnode.name, mixin) + mixin } } \ No newline at end of file diff --git a/src/codechicken/multipart/asm/ASMMixinFactory.scala b/src/codechicken/multipart/asm/ASMMixinFactory.scala index 01a37a4..a914b6c 100644 --- a/src/codechicken/multipart/asm/ASMMixinFactory.scala +++ b/src/codechicken/multipart/asm/ASMMixinFactory.scala @@ -59,11 +59,12 @@ class ASMMixinFactory[T](val baseType:Class[T], private val paramTypes:Class[_]* case None => } - def getParents(info:ClassInfo):Seq[ClassInfo] = info.superClass.toSeq.flatMap(i => i +: getParents(i)) - if(!getParents(cnode).map(_.name).contains(baseType.nodeName)) + val info = getClassInfo(cnode) + def checkParent(info:ClassInfo):Boolean = info.superClass.exists(i => i.name == baseType.nodeName || checkParent(i)) + if(!checkParent(info)) throw new IllegalArgumentException("Mixin trait "+s_trait+" must extend "+baseType.nodeName) - if(isScala(cnode) && isTrait(cnode)) { + if(info.isTrait) { registerScalaTrait(cnode) } else { diff --git a/src/codechicken/multipart/asm/ScalaSignature.scala b/src/codechicken/multipart/asm/ScalaSignature.scala index 708a1eb..ef2be79 100644 --- a/src/codechicken/multipart/asm/ScalaSignature.scala +++ b/src/codechicken/multipart/asm/ScalaSignature.scala @@ -33,8 +33,9 @@ object ScalaSignature def isPrivate = hasFlag(0x00000004) def isProtected = hasFlag(0x00000008) def isAbstract = hasFlag(0x00000080) - def isInterface = hasFlag(0x00000800) + def isDeferred = hasFlag(0x00000100)//abstract for methods def isMethod = hasFlag(0x00000200) + def isInterface = hasFlag(0x00000800) def isParam = hasFlag(0x00002000) def isStatic = hasFlag(0x00800000) def isTrait = hasFlag(0x02000000) @@ -47,17 +48,25 @@ object ScalaSignature def flags:Int def hasFlag(flag:Int) = (flags&flag) != 0 } - - case class ClassSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef + + trait ClassSymbolRef extends SymbolRef { - override def toString = "ClassSymbol("+name+","+owner+","+flags.toHexString+","+info+")" + def name:String + def owner:SymbolRef + def flags:Int + def info:Int + def full = owner.full+"."+name - + override def toString = getClass.getName+"("+name+","+owner+","+flags.toHexString+","+info+")" + def info(sig:ScalaSignature):ClassType = sig.evalT(info) def jParent(sig:ScalaSignature) = info(sig).parent.jName def jInterfaces(sig:ScalaSignature) = info(sig).interfaces.map(_.jName) } + case class ClassSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends ClassSymbolRef + case class ObjectSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends ClassSymbolRef + case class MethodSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef { override def toString = "MethodSymbol("+name+","+owner+","+flags.toHexString+","+info+")" @@ -102,13 +111,15 @@ object ScalaSignature trait TypeRef { - def name:String + def sym:SymbolRef + def name = sym.full + def jName = name.replace('.', '/') match { - case "scala/AnyRef" => "java/lang/Object" + case "scala/AnyRef" | "scala/Any" => "java/lang/Object" case s => s } - def jDesc:String = name match - { + + def jDesc = name match { case "scala.Array" => null case "scala.Long" => "J" case "scala.Int" => "I" @@ -128,36 +139,30 @@ object ScalaSignature def returnType = this - def name = sym.full - - override def jDesc = name match - { + override def jDesc = name match { case "scala.Array" => "["+typArgs(0).jDesc case _ => super.jDesc } } case class ThisType(sym:SymbolRef) extends TypeRef - { - def name = sym.full - } - + case class SingleType(owner:TypeRef, sym:SymbolRef) extends TypeRef { - def name = sym.full + override def jName = super.jName+"$" } - + case object NoType extends TypeRef { - def name = "" + def sym = null + override def name = "" } class SigEntry(val start:Int, val bytes:Bytes) { def id = bytes.bytes(start) - def delete() - { + def delete() { bytes.bytes(start) = 3 } } @@ -168,8 +173,7 @@ case class ScalaSignature(major:Int, minor:Int, table:Array[SigEntry], bytes:Byt val e = table(i) val bc = e.bytes val bcr = bc:ByteCodeReader - return e.id match - { + e.id match { case 1|2 => bcr.readString(bc.len) case 3 => NoSymbol.full case 9|10 => @@ -182,22 +186,21 @@ case class ScalaSignature(major:Int, minor:Int, table:Array[SigEntry], bytes:Byt def evalT[T](i:Int):T = eval(i).asInstanceOf[T] - def evalList[T](bcr:ByteCodeReader) = - { + def evalList[T](bcr:ByteCodeReader) = { var l = MList[T]() while(bcr.more) l+=evalT(bcr.readNat) l.toList } - def eval(i:Int):Any = {//we only parse the ones we actually care about + def eval(i:Int):Any = {//only parse the ones that matter for this project val e = table(i) val bc = e.bytes val bcr = bc:ByteCodeReader - return e.id match - { + e.id match { case 1|2 => evalS(i) case 6 => ClassSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat) + case 7 => ObjectSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat) case 8 => MethodSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat) case 9|10 => ExternalSymbol(evalS(i)) case 11|12 => NoType //12 is actually NoPrefixType (no lower bound) @@ -206,7 +209,7 @@ case class ScalaSignature(major:Int, minor:Int, table:Array[SigEntry], bytes:Byt case 16 => TypeRefType(evalT(bcr.readNat), evalT(bcr.readNat), evalList(bcr)) case 19 => ClassType(evalT(bcr.readNat), evalList(bcr)) case 20 => MethodType(evalT(bcr.readNat), evalList(bcr)) - case 21|48 => ParameterlessType(evalT(bcr.readNat))//48 is actually a bounded super type, but it should work fine for our purposes + case 21|48 => ParameterlessType(evalT(bcr.readNat))//48 is actually a bounded super type, but it should work fine for this project case _ => NoSymbol } } @@ -222,37 +225,32 @@ class ByteCodeReader(val bc:Bytes) def readByte = advance(1)(bc.bytes(pos)) - def readNat:Int = - { + def readNat = { var r = 0 var b = 0 - do - { + do { b = readByte r = r<<7|b&0x7F } while((b&0x80) != 0) - return r + r } - def advance[A](len:Int)(r:A):A = - { + def advance[A](len:Int)(r:A) = { if(pos+len > bc.pos+bc.len) throw new IllegalArgumentException("Ran off the end of bytecode") pos+=len - return r + r } - def readEntry = - { + def readEntry = { val p = pos - val tpe:Int = readByte + val tpe = readByte val len = readNat advance(len)(new SigEntry(p, new Bytes(bc.bytes, pos, len))) } - def readSig = - { + def readSig = { val major = readByte val minor = readByte val table = new Array[SigEntry](readNat) @@ -264,22 +262,19 @@ class ByteCodeReader(val bc:Bytes) object ScalaSigReader { - def decode(s:String):Array[Byte] = - { + def decode(s:String) = { val bytes = s.getBytes - return bytes take ByteCodecs.decode(bytes) + bytes take ByteCodecs.decode(bytes) } - def encode(b:Array[Byte]):String = - { + def encode(b:Array[Byte]) = { val bytes = ByteCodecs.encode8to7(b) var i = 0 - while(i < bytes.length) - { + while(i < bytes.length) { bytes(i) = ((bytes(i)+1)&0x7F).toByte i+=1 } - return new String(bytes.take(bytes.length-1), "UTF-8") + new String(bytes.take(bytes.length-1), "UTF-8") } def read(ann:AnnotationNode):ScalaSignature = Bytes(decode(ann.values.get(1).asInstanceOf[String])).readSig @@ -294,8 +289,7 @@ object ScalaSigReader class ScalaSigSideTransformer { - def transform(ann:AnnotationNode, cnode:ClassNode, removedFields:JList[FieldNode], removedMethods:JList[MethodNode]) - { + def transform(ann:AnnotationNode, cnode:ClassNode, removedFields:JList[FieldNode], removedMethods:JList[MethodNode]) { if(removedFields.isEmpty && removedMethods.isEmpty) return @@ -303,35 +297,28 @@ class ScalaSigSideTransformer val remMethods = removedMethods.asScala.map(f => (f.name, f.desc.replace('$', '/'))) val sig = ScalaSigReader.read(ann) - for(i <- 0 until sig.table.length) - { + for(i <- 0 until sig.table.length) { val e = sig.table(i) - if(e.id == 8)//check and remove - { - val sym:MethodSymbol = sig.evalT(i) - if(sym.isAccessor) - { - val fName = if(sym.name.endsWith("_$eq")) sym.name.substring(0, sym.name.length-4) else sym.name - if(remFields.find(t => t._1 == sym.name.trim).nonEmpty) - { + if (e.id == 8) {//check and remove + val sym: MethodSymbol = sig.evalT(i) + if (sym.isAccessor) { + val fName = if (sym.name.endsWith("_$eq")) sym.name.substring(0, sym.name.length - 4) else sym.name + if (remFields.find(t => t._1 == sym.name.trim).nonEmpty) { e.delete() val it = cnode.methods.iterator - while(it.hasNext) - { + while (it.hasNext) { val m = it.next - if(m.name == sym.name && m.desc == sym.jDesc(sig)) + if (m.name == sym.name && m.desc == sym.jDesc(sig)) it.remove() } } } - else if(sym.isMethod) - { - if(remMethods.find(t => t._1 == sym.name && t._2 == sym.jDesc(sig)).nonEmpty) + else if (sym.isMethod) { + if (remMethods.find(t => t._1 == sym.name && t._2 == sym.jDesc(sig)).nonEmpty) e.delete() } - else//field - { - if(remFields.find(t => t._1 == sym.name.trim).nonEmpty) + else {//field + if (remFields.find(t => t._1 == sym.name.trim).nonEmpty) e.delete() } } diff --git a/src/codechicken/multipart/nei/NEI_MicroblockConfig.java b/src/codechicken/multipart/nei/NEI_MicroblockConfig.java index 60169da..89b5f27 100644 --- a/src/codechicken/multipart/nei/NEI_MicroblockConfig.java +++ b/src/codechicken/multipart/nei/NEI_MicroblockConfig.java @@ -2,7 +2,7 @@ package codechicken.multipart.nei; import codechicken.lib.inventory.InventoryUtils; import codechicken.microblock.MicroblockClass; -import codechicken.microblock.MicroblockClassRegistry; +import codechicken.microblock.CommonMicroClass; import codechicken.microblock.handler.MicroblockProxy; import codechicken.nei.ItemStackMap; import codechicken.nei.api.API; @@ -15,7 +15,7 @@ public class NEI_MicroblockConfig implements IConfigureNEI { @Override public void loadConfig() { - MicroblockClass[] microClasses = MicroblockClassRegistry.classes(); + MicroblockClass[] microClasses = CommonMicroClass.classes(); for (int c = 0; c < microClasses.length; c++) { MicroblockClass mcrClass = microClasses[c]; if (mcrClass == null)