Added MicroblockGenerator, micro materials can now mix traits into the microblock class itself

Improved mixin compilation a bunch
This commit is contained in:
Chicken-Bones committed 2015-05-07 16:04:09 +10:00
1 parent a621ddbe84
commit 00e6a9bb82
17 files changed
+396 -348

No files matched your search

@@ -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
+13 -21
View File
@@ -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 =
{
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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<<s) == 0) {
face.loadCuboidFace(c, s)
mat.renderMicroFace(pos, pass, c)
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
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<<getSlot
def getPartialOcclusionBoxes = Seq(getBounds)
override def itemClassID = microClass.classID
def getResistanceFactor = microClass.getResistanceFactor
override def itemClassID = microClass.getClassId
}
@@ -1,18 +1,57 @@
package codechicken.microblock
trait MicroblockClass
{
var classID:Int = _
def itemSlot = 3
def getName:String
def create(client:Boolean, material:Int):CommonMicroblock
def placementProperties:PlacementProperties
def register(id:Int) = MicroblockClassRegistry.registerMicroClass(this, id)
import codechicken.multipart.{TMultiPart, MultiPartRegistry}
import codechicken.multipart.MultiPartRegistry.IPartFactory2
import net.minecraft.nbt.NBTTagCompound
import codechicken.lib.data.MCDataInput
abstract class MicroblockClass extends IPartFactory2
{
def getName:String
def baseTrait:Class[_ <: Microblock]
def clientTrait:Class[_ <: MicroblockClient]
def getResistanceFactor:Float
val baseTraitId = MicroblockGenerator.registerTrait(baseTrait)
val clientTraitId = MicroblockGenerator.registerTrait(clientTrait)
def register() {
MultiPartRegistry.registerParts(this, getName)
}
def create(client:Boolean, material:Int) = MicroblockGenerator.create(this, material, client)
override def createPart(name: String, packet: MCDataInput) = create(true, MicroMaterialRegistry.readMaterialID(packet))
override def createPart(name: String, nbt: NBTTagCompound) = create(false, MicroMaterialRegistry.materialID(nbt.getString("material")))
}
/**
* Microblocks with corresponding items
*/
abstract class CommonMicroClass extends MicroblockClass
{
private var classId:Int = _
def getClassId = classId
def itemSlot:Int
def placementProperties:PlacementProperties
def register(id:Int) {
register()
classId = id
CommonMicroClass.registerMicroClass(this, id)
}
}
object CommonMicroClass
{
val classes = new Array[CommonMicroClass](256)
def getMicroClass(modelId:Int) = classes(modelId>>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
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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)
@@ -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<<s) == 0) {
face.loadCuboidFace(c, s)
mat.renderMicroFace(pos, pass, c)
}
}
}
@@ -31,7 +31,7 @@ object MultipartGenerator
private val clientTraitId = MultipartMixinFactory.registerTrait(classOf[TileMultipartClient])
//scratch bitset
private val bitset = new BitSet()
private val bitset = new BitSet
private def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
private def interfaceTraitMap(client:Boolean) = if(client) interfaceTraitMap_c else interfaceTraitMap_s
@@ -49,9 +49,6 @@ object DebugPrinter
object ASMMixinCompiler
{
import StackAnalyser.width
val cl = getClass.getClassLoader.asInstanceOf[LaunchClassLoader]
val m_defineClass = classOf[ClassLoader].getDeclaredMethod("defineClass", classOf[Array[Byte]], Integer.TYPE, Integer.TYPE)
val m_runTransformers = classOf[LaunchClassLoader].getDeclaredMethod("runTransformers", classOf[String], classOf[String], classOf[Array[Byte]])
@@ -79,7 +76,7 @@ object ASMMixinCompiler
def getBytes(name: String): Array[Byte] = {
val jName = name.replace('/', '.')
if (jName.equals("java.lang.Object"))
if (jName == "java.lang.Object")
return null
def useTransformers = f_transformerExceptions.get(cl).asInstanceOf[JSet[String]]
@@ -108,13 +105,6 @@ object ASMMixinCompiler
}
}
def isScala(cnode: ClassNode) = ScalaSigReader.ann(cnode).isDefined
def isTrait(cnode: ClassNode) = {
val csym: ClassSymbol = ScalaSigReader.read(ScalaSigReader.ann(cnode).get).evalT(0)
csym.isTrait && !csym.isInterface
}
def getMixinInfo(name: String) = mixinMap.get(name)
case class FieldMixin(name: String, desc: String, access: Int)
@@ -137,6 +127,7 @@ object ASMMixinCompiler
def owner: ClassInfo
def name: String
def desc: String
def exceptions: Array[String]
def isPrivate: Boolean
def isAbstract: Boolean
@@ -147,15 +138,19 @@ object ASMMixinCompiler
{
def name: String
def superClass: Option[ClassInfo]
def interfaces: Seq[ClassInfo]
def methods: Seq[MethodInfo]
def interfaces: Iterable[ClassInfo]
def methods: Iterable[MethodInfo]
override def toString = getClass.getSimpleName+"("+name+")"
override def toString = getClass.getName.replaceAll(".+[\\$\\.]", "")+"("+name+")"
def getMethod(name:String, desc:String) = methods.find(m => 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, "<init>", "()V", null, null)
minit.visitVarInsn(ALOAD, 0)
minit.visitMethodInsn(INVOKESPECIAL, cnode.superName, "<init>", "()V", false)
val cinit = baseInfo.methods.find(_.name == "<init>").get
val minit = cnode.visitMethod(ACC_PUBLIC, "<init>", cinit.desc, null, null).asInstanceOf[MethodNode]
writeBridge(minit, cinit.desc, INVOKESPECIAL, superClass, "<init>", 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("<clinit>"))
if (mnode.name == "<clinit>")
throw new IllegalArgumentException("Static initialisers are not permitted " + cnode.name + " as a mixin trait")
if (mnode.name.equals("<init>")) {
if (!mnode.desc.equals("()V"))
if (mnode.name == "<init>") {
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
}
}
@@ -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 {
@@ -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 = "<no type>"
def sym = null
override def name = "<no type>"
}
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()
}
}
@@ -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)