Use FML logging system.

Made some of the ASM code more concise
This commit is contained in:
Chicken-Bones committed 2014-04-28 13:34:20 +10:00
1 parent 5c4b9e0a5b
commit bd1a72e958
13 files changed
+388 -451

No files matched your search

+10 -10
View File
@@ -109,8 +109,8 @@ object ConfigContent
catch
{
case e:Exception =>
System.err.println("Invalid line in microblocks.cfg: "+s)
System.err.println(e.getMessage)
logger.error("Invalid line in microblocks.cfg: "+s)
logger.error(e.getMessage)
}
}
}
@@ -126,7 +126,7 @@ object ConfigContent
if(idMap(i) != null)
{
if(block == Blocks.air)
System.err.println("Warning: Unable to add micro material for block with ID "+i+" as it doesn't exist")
logger.warn("Unable to add micro material for block with ID "+i+" as it doesn't exist")
else
createAndRegister(block, idMap(i))
}
@@ -142,10 +142,10 @@ object ConfigContent
createAndRegister(block, m)
}
catch {
case e:IllegalStateException => System.err.println("Unable to register micro material: "+
case e:IllegalStateException => logger.error("Unable to register micro material: "+
materialKey(block, m)+"\n\t"+e.getMessage)
case e:Exception =>
System.err.println("Unable to register micro material: "+materialKey(block, m))
logger.error("Unable to register micro material: "+materialKey(block, m))
e.printStackTrace()
}
}
@@ -153,14 +153,14 @@ object ConfigContent
}
}
nameMap.foreach(e => System.err.println("Warning: Unable to add micro material for block with unlocalised name "+e._1+" as it doesn't exist"))
nameMap.foreach(e => logger.warn("Unable to add micro material for block with unlocalised name "+e._1+" as it doesn't exist"))
}
def handleIMC(messages:Seq[IMCMessage]) {
messages.filter(_.key == "microMaterial").foreach{msg =>
def error(s:String) {
System.err.println("Invalid microblock IMC message from "+msg.getSender+": "+s)
logger.error("Invalid microblock IMC message from "+msg.getSender+": "+s)
}
if(msg.getMessageType != classOf[ItemStack])
@@ -168,15 +168,15 @@ object ConfigContent
else {
val stack = msg.getItemStackValue
if(Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
System.err.println("Invalid Block: "+stack.getItem)
error("Invalid Block: "+stack.getItem)
else if(stack.getItemDamage < 0 || stack.getItemDamage >= 16)
System.err.println("Invalid metadata: "+stack.getItemDamage)
error("Invalid metadata: "+stack.getItemDamage)
else {
try {
createAndRegister(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)
}
catch {
case e:IllegalStateException => System.err.println("Unable to register micro material: "+
case e:IllegalStateException => error("Unable to register micro material: "+
materialKey(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)+"\n\t"+e.getMessage)
}
}
@@ -21,7 +21,6 @@ import net.minecraft.block.Block.SoundType
object MicroMaterialRegistry
{
/**
* Interface for defining a micro material
*/
@@ -127,11 +126,11 @@ object MicroMaterialRegistry
throw new IllegalStateException("You must register your materials in the init methods.")
if (typeMap.contains(name)) {
System.err.println("Material with id " + name + " is already registered.")
logger.error("Material with id " + name + " is already registered.")
return
}
System.out.println("Registered micro material: " + name)
logger.info("Registered micro material: " + name)
typeMap.put(name, material)
}
@@ -144,9 +143,9 @@ object MicroMaterialRegistry
throw new IllegalStateException("You must register your materials in the init methods.")
if (typeMap.remove(name).isEmpty)
System.err.println("Material with id " + name + " is was not registered.")
logger.error("Material with id " + name + " is was not registered.")
System.out.println("Replaced micro material: " + name)
logger.info("Replaced micro material: " + name)
typeMap.put(name, material)
}
@@ -215,7 +214,7 @@ object MicroMaterialRegistry
def materialID(name: String) = nameMap.get(name) match {
case Some(v) => v
case None =>
System.err.println("Missing mapping for part with ID: " + name)
logger.error("Missing mapping for part with ID: " + name)
0
}
@@ -12,41 +12,35 @@ import codechicken.microblock.ConfigContent
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent
import scala.collection.JavaConversions._
@Mod(modid = "ForgeMicroblock", acceptedMinecraftVersions = "[1.7.2]",
dependencies="required-after:ForgeMultipart;after:*", modLanguage="scala")
@Mod(modid = "ForgeMicroblock", acceptedMinecraftVersions = "[1.7.2]", dependencies = "required-after:ForgeMultipart;after:*", modLanguage = "scala")
object MicroblockMod
{
@EventHandler
def preInit(event:FMLPreInitializationEvent)
{
MicroblockProxy.preInit()
def preInit(event: FMLPreInitializationEvent) {
MicroblockProxy.preInit(event.getModLog)
DefaultContent.load()
ConfigContent.parse(event.getModConfigurationDirectory)
}
@EventHandler
def init(event:FMLInitializationEvent)
{
def init(event: FMLInitializationEvent) {
MicroblockProxy.init()
ConfigContent.load()
}
@EventHandler
def postInit(event:FMLPostInitializationEvent)
{
def postInit(event: FMLPostInitializationEvent) {
MicroMaterialRegistry.setupIDMap()
MicroblockProxy.postInit()
}
@EventHandler
def beforeServerStart(event:FMLServerAboutToStartEvent)
{
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MicroMaterialRegistry.setupIDMap()
}
@EventHandler
def handleIMC(event:IMCEvent)
{
def handleIMC(event: IMCEvent) {
ConfigContent.handleIMC(event.getMessages)
}
}
@@ -20,9 +20,12 @@ import cpw.mods.fml.common.registry.GameRegistry
import cpw.mods.fml.common.network.NetworkRegistry
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.init.{Blocks, Items}
import org.apache.logging.log4j.Logger
class MicroblockProxy_serverImpl
{
var logger:Logger = _
private var baseID = 24875-1
def nextItemID = {baseID+=1; baseID}
@@ -33,8 +36,9 @@ class MicroblockProxy_serverImpl
var sawDiamond:Item = _
var stoneRod:Item = _
def preInit()
def preInit(logger:Logger)
{
this.logger = logger
itemMicro = new ItemMicroPart
GameRegistry.registerItem(itemMicro, "microblock")
sawStone = createSaw(config, "sawStone", 1)
+8
View File
@@ -0,0 +1,8 @@
package codechicken
import codechicken.microblock.handler.MicroblockProxy
package object microblock
{
def logger = MicroblockProxy.logger
}
@@ -162,7 +162,7 @@ object MultiPartRegistry
if (part.isDefined)
return part.get(client)
else {
System.err.println("Missing mapping for part with ID: " + name)
logger.error("Missing mapping for part with ID: " + name)
return null
}
}
@@ -159,7 +159,7 @@ object MultipartGenerator
if(c_trait != null)
{
if(interfaceTraitMap_c.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
logger.error("Trait already registered for "+s_interface)
else
{
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->c_trait)
@@ -169,7 +169,7 @@ object MultipartGenerator
if(s_trait != null)
{
if(interfaceTraitMap_s.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
logger.error("Trait already registered for "+s_interface)
else
{
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->s_trait)
@@ -198,14 +198,14 @@ object MultipartGenerator
if(client)
{
if(interfaceTraitMap_c.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
logger.error("Trait already registered for "+s_interface)
else
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->tType)
}
if(server)
{
if(interfaceTraitMap_s.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
logger.error("Trait already registered for "+s_interface)
else
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->tType)
}
@@ -6,62 +6,53 @@ import scala.collection.JavaConversions._
import org.objectweb.asm.tree._
import org.objectweb.asm.Opcodes._
import org.objectweb.asm.ClassReader
import org.objectweb.asm.util.TraceClassVisitor
import org.objectweb.asm.util.Textifier
import org.objectweb.asm.Type
import org.objectweb.asm.MethodVisitor
import Type._
import codechicken.lib.asm.ASMHelper._
import codechicken.lib.asm.ObfMapping
import codechicken.lib.asm.{InsnListSection, InsnComparator, ASMHelper, ObfMapping}
import java.io.File
import java.io.PrintWriter
import ScalaSignature._
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import net.minecraft.launchwrapper.LaunchClassLoader
import codechicken.multipart.handler.MultipartProxy
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper
import org.apache.logging.log4j.LogManager
object DebugPrinter
{
val debug = MultipartProxy.config.getTag("debug_asm").getBooleanValue(!ObfMapping.obfuscated)
val logger = LogManager.getLogger("Multipart ASM")
private var permGenUsed = 0
val dir = new File("asm/multipart")
if(debug)
{
if(!dir.exists)
if (debug) {
if (!dir.exists)
dir.mkdirs()
for(file <- dir.listFiles)
for (file <- dir.listFiles)
file.delete
}
def dump(name:String, bytes:Array[Byte])
{
if(!debug) return
val fileout = new File(dir, name.replace('/', '#')+".txt")
val pout = new PrintWriter(fileout)
new ClassReader(bytes).accept(new TraceClassVisitor(null, new Textifier(), pout), 0)
pout.close()
def dump(name: String, bytes: Array[Byte]) {
if (debug) ASMHelper.dump(bytes, new File(dir, name.replace('/', '#') + ".txt"), false, false)
}
def defined(name:String, bytes:Array[Byte])
{
if((permGenUsed+bytes.length)/16000 != permGenUsed/16000)
log((permGenUsed+bytes.length)+" bytes of permGen has been used by ASMMixinCompiler")
def defined(name: String, bytes: Array[Byte]) {
if ((permGenUsed + bytes.length) / 16000 != permGenUsed / 16000)
log((permGenUsed + bytes.length) + " bytes of permGen has been used by ASMMixinCompiler")
permGenUsed += bytes.length
}
def log(msg:String) = if(debug) println(msg)
def log(msg: String) = if (debug) println(msg)
}
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]])
@@ -69,141 +60,141 @@ object ASMMixinCompiler
m_defineClass.setAccessible(true)
m_runTransformers.setAccessible(true)
f_transformerExceptions.setAccessible(true)
private val traitByteMap = MMap[String, Array[Byte]]()
private val mixinMap = MMap[String, MixinInfo]()
def define(name:String, bytes:Array[Byte]) =
{
def define(name: String, bytes: Array[Byte]) = {
internalDefine(name, bytes)
DebugPrinter.defined(name, bytes)
try {
m_defineClass.invoke(cl, bytes, 0:Integer, bytes.length:Integer).asInstanceOf[Class[_]]
m_defineClass.invoke(cl, bytes, 0: Integer, bytes.length: Integer).asInstanceOf[Class[_]]
} catch {
case link:LinkageError if link.getMessage.contains("duplicate") =>
throw new IllegalStateException("class with name: "+name+" already loaded. Do not reference your mixin classes before registering them with MultipartGenerator", link)
case link: LinkageError if link.getMessage.contains("duplicate") =>
throw new IllegalStateException("class with name: " + name + " already loaded. Do not reference your mixin classes before registering them with MultipartGenerator", link)
}
}
getBytes("cpw/mods/fml/common/asm/FMLSanityChecker")
def getBytes(name:String):Array[Byte] =
{
def getBytes(name: String): Array[Byte] = {
val jName = name.replace('/', '.')
if(jName.equals("java.lang.Object"))
if (jName.equals("java.lang.Object"))
return null
def useTransformers = f_transformerExceptions.get(cl).asInstanceOf[JSet[String]]
.find(jName.startsWith).isEmpty
val obfName = if(ObfMapping.obfuscated) FMLDeobfuscatingRemapper.INSTANCE.unmap(name).replace('/', '.') else jName
.find(jName.startsWith).isEmpty
val obfName = if (ObfMapping.obfuscated) FMLDeobfuscatingRemapper.INSTANCE.unmap(name).replace('/', '.') else jName
val bytes = cl.getClassBytes(obfName)
if(bytes != null && useTransformers)
if (bytes != null && useTransformers)
return m_runTransformers.invoke(cl, jName, obfName, bytes).asInstanceOf[Array[Byte]]
return bytes
}
def internalDefine(name:String, bytes:Array[Byte])
{
def internalDefine(name: String, bytes: Array[Byte]) {
traitByteMap.put(name.replace('.', '/'), bytes)
BaseNodeInfo.clear(name)
DebugPrinter.dump(name, bytes)
}
def classNode(name:String) = traitByteMap.getOrElseUpdate(name.replace('.', '/'), getBytes(name.replace('.', '/'))) match {
def classNode(name: String) = traitByteMap.getOrElseUpdate(name.replace('.', '/'), getBytes(name.replace('.', '/'))) match {
case null => null
case v => createClassNode(v, ClassReader.EXPAND_FRAMES)
}
def isScala(cnode:ClassNode) = ScalaSigReader.ann(cnode).isDefined
def isTrait(cnode:ClassNode) =
{
val csym:ClassSymbol = ScalaSigReader.read(ScalaSigReader.ann(cnode).get).evalT(0)
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)
def getMixinInfo(name: String) = mixinMap.get(name)
case class FieldMixin(name: String, desc: String, access: Int)
{
def accessName(owner:String) = if((access & ACC_PRIVATE) != 0)
owner.replace('/', '$')+"$$"+name
else
name
def accessName(owner: String) = if ((access & ACC_PRIVATE) != 0)
owner.replace('/', '$') + "$$" + name
else
name
}
case class MixinInfo(name:String, parent:String, fields:Seq[FieldMixin],
methods:Seq[MethodNode], supers:Map[String, String])
case class MixinInfo(name: String, parent: String, fields: Seq[FieldMixin],
methods: Seq[MethodNode], supers: Map[String, String])
{
def tname = name+"$class"
def tname = name + "$class"
}
object BaseNodeInfo
{
private val baseNodeMap = MMap[String, BaseNodeInfo]()
def clear(name:String) = baseNodeMap.remove(name)
def getNodeInfo(name:String) = baseNodeMap.getOrElseUpdate(name, new BaseNodeInfo(name))
private def getNodeInfo(clazz:ClassInfoSource) = clazz match {
def clear(name: String) = baseNodeMap.remove(name)
def getNodeInfo(name: String) = baseNodeMap.getOrElseUpdate(name, new BaseNodeInfo(name))
private def getNodeInfo(clazz: ClassInfoSource) = clazz match {
case null => null
case v => baseNodeMap.getOrElseUpdate(v.name, new BaseNodeInfo(v))
}
case class MethodNodeInfo(owner:String, m:MethodInfoSource)
case class MethodNodeInfo(owner: String, m: MethodInfoSource)
{
def name = m.name
def desc = m.desc
}
trait MethodInfoSource
{
def name:String
def desc:String
def isPrivate:Boolean
def isAbstract:Boolean
def name: String
def desc: String
def isPrivate: Boolean
def isAbstract: Boolean
}
trait ClassInfoSource
{
def name:String
def superClass:Option[BaseNodeInfo]
def interfaces:Seq[BaseNodeInfo]
def methods:Seq[MethodInfoSource]
def name: String
def superClass: Option[BaseNodeInfo]
def interfaces: Seq[BaseNodeInfo]
def methods: Seq[MethodInfoSource]
}
implicit def JClassInfoSource(clazz:Class[_]) = if(clazz == null) null else new JClassInfoSource(clazz)
class JClassInfoSource(clazz:Class[_]) extends ClassInfoSource
implicit def JClassInfoSource(clazz: Class[_]) = if (clazz == null) null else new JClassInfoSource(clazz)
class JClassInfoSource(clazz: Class[_]) extends ClassInfoSource
{
case class JMethodInfoSource(method:Method) extends MethodInfoSource
case class JMethodInfoSource(method: Method) extends MethodInfoSource
{
def name = method.getName
def desc = getType(method).getDescriptor
def isPrivate = Modifier.isPrivate(method.getModifiers)
def isAbstract = Modifier.isAbstract(method.getModifiers)
}
def name = clazz.getName.replace('.', '/')
def superClass = Option(getNodeInfo(clazz.getSuperclass))
def interfaces = clazz.getInterfaces.map(getNodeInfo(_))
def methods = clazz.getMethods.map(JMethodInfoSource(_))
}
implicit def ClassNodeInfoSource(cnode:ClassNode) = if(cnode == null) null else new ClassNodeInfoSource(cnode)
class ClassNodeInfoSource(cnode:ClassNode) extends ClassInfoSource
implicit def ClassNodeInfoSource(cnode: ClassNode) = if (cnode == null) null else new ClassNodeInfoSource(cnode)
class ClassNodeInfoSource(cnode: ClassNode) extends ClassInfoSource
{
case class MethodNodeInfoSource(mnode:MethodNode) extends MethodInfoSource
case class MethodNodeInfoSource(mnode: MethodNode) extends MethodInfoSource
{
def name = mnode.name
def desc = mnode.desc
def isPrivate = (mnode.access & ACC_PRIVATE) != 0
def isAbstract = (mnode.access & ACC_ABSTRACT) != 0
}
def name = cnode.name
def superClass = Option(getNodeInfo(cnode.superName))
def interfaces = cnode.interfaces match {
@@ -212,17 +203,17 @@ object ASMMixinCompiler
}
def methods = cnode.methods.map(MethodNodeInfoSource(_))
}
class ScalaNodeInfoSource(cnode:ClassNode) extends ClassNodeInfoSource(cnode)
class ScalaNodeInfoSource(cnode: ClassNode) extends ClassNodeInfoSource(cnode)
{
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
val csym = sig.evalT(0):ClassSymbol
val csym = sig.evalT(0): ClassSymbol
override def superClass = Option(getNodeInfo(csym.jParent(sig)))
override def interfaces = csym.jInterfaces(sig).map(getNodeInfo)
}
def classInfo(name:String):ClassInfoSource = name match {
def classInfo(name: String): ClassInfoSource = name match {
case null => null
case s => classNode(s) match {
case null => cl.findClass(s.replace('/', '.'))
@@ -231,239 +222,229 @@ object ASMMixinCompiler
}
}
}
import BaseNodeInfo._
class BaseNodeInfo(val clazz:ClassInfoSource)
class BaseNodeInfo(val clazz: ClassInfoSource)
{
def this(name:String) = this(classInfo(name))
val publicmethods = exportedMethods(clazz)//map of nameDesc to owner+method
def exportedMethods(o:ClassInfoSource):Map[String, MethodNodeInfo] =
{
val eMethods = o.methods.filter(!_.isPrivate)//defined accessible methods
.map(m => (m.name+m.desc, MethodNodeInfo(o.name, m))).toMap
eMethods ++ (o.superClass++o.interfaces).flatMap(_.publicmethods)
def this(name: String) = this(classInfo(name))
val publicmethods = exportedMethods(clazz) //map of nameDesc to owner+method
def exportedMethods(o: ClassInfoSource): Map[String, MethodNodeInfo] = {
val eMethods = o.methods.filter(!_.isPrivate) //defined accessible methods
.map(m => (m.name + m.desc, MethodNodeInfo(o.name, m))).toMap
eMethods ++ (o.superClass ++ o.interfaces).flatMap(_.publicmethods)
}
}
def finishBridgeCall(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
{
def finishBridgeCall(mv: MethodVisitor, mvdesc: String, opcode: Int, owner: String, name: String, desc: String) {
val args = getArgumentTypes(mvdesc)
val ret = getReturnType(mvdesc)
var localIndex = 1
args.foreach{arg =>
mv.visitVarInsn(arg.getOpcode(ILOAD), localIndex)
localIndex+=width(arg)
args.foreach {
arg =>
mv.visitVarInsn(arg.getOpcode(ILOAD), localIndex)
localIndex += width(arg)
}
mv.visitMethodInsn(opcode, owner, name, desc)
mv.visitInsn(ret.getOpcode(IRETURN))
mv.visitMaxs(Math.max(width(args)+1, width(ret)), width(args)+1)
mv.visitMaxs(Math.max(width(args) + 1, width(ret)), width(args) + 1)
}
def writeBridge(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
{
def writeBridge(mv: MethodVisitor, mvdesc: String, opcode: Int, owner: String, name: String, desc: String) {
mv.visitVarInsn(ALOAD, 0)
finishBridgeCall(mv, mvdesc, opcode, owner, name, desc)
}
def writeStaticBridge(mv:MethodNode, mname:String, t:MixinInfo) =
def writeStaticBridge(mv: MethodNode, mname: String, t: MixinInfo) =
writeBridge(mv, mv.desc, INVOKESTATIC, t.tname, mname, staticDesc(t.name, mv.desc))
def mixinClasses(name:String, superClass:String, traits:Seq[String]) =
{
def mixinClasses(name: String, superClass: String, traits: Seq[String]) = {
val traitInfos = traits.map(t => mixinMap(t.replace('.', '/')))
val cnode = new ClassNode()
cnode.visit(V1_6, ACC_PUBLIC, name, null, superClass.replace('.', '/'), traitInfos.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")
val prevInfos = MList[MixinInfo]()
traitInfos.foreach{ t =>
minit.visitVarInsn(ALOAD, 0)
minit.visitMethodInsn(INVOKESTATIC, t.tname, "$init$", "(L"+t.name+";)V")
t.fields.foreach{ f =>
val fv = cnode.visitField(ACC_PRIVATE, f.accessName(t.name), f.desc, null, null).asInstanceOf[FieldNode]
val ftype = getType(fv.desc)
var mv = cnode.visitMethod(ACC_PUBLIC, fv.name, "()"+f.desc, null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, name, fv.name, fv.desc)
mv.visitInsn(ftype.getOpcode(IRETURN))
mv.visitMaxs(1, 1)
mv = cnode.visitMethod(ACC_PUBLIC, fv.name+"_$eq", "("+f.desc+")V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ftype.getOpcode(ILOAD), 1)
mv.visitFieldInsn(PUTFIELD, name, fv.name, fv.desc)
mv.visitInsn(RETURN)
mv.visitMaxs(width(ftype)+1, width(ftype)+1)
}
t.supers.foreach{ s =>
val (name, desc) = seperateDesc(s._1)
val mv = cnode.visitMethod(ACC_PUBLIC, t.name.replace('/', '$')+"$$super$"+name, desc, null, null).asInstanceOf[MethodNode]
prevInfos.reverse.find(t => t.supers.contains(s._1)) match {//each super goes to the one before
case Some(st) => writeStaticBridge(mv, name, st)
case None => writeBridge(mv, desc, INVOKESPECIAL, s._2, name, desc)
traitInfos.foreach {
t =>
minit.visitVarInsn(ALOAD, 0)
minit.visitMethodInsn(INVOKESTATIC, t.tname, "$init$", "(L" + t.name + ";)V")
t.fields.foreach {
f =>
val fv = cnode.visitField(ACC_PRIVATE, f.accessName(t.name), f.desc, null, null).asInstanceOf[FieldNode]
val ftype = getType(fv.desc)
var mv = cnode.visitMethod(ACC_PUBLIC, fv.name, "()" + f.desc, null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, name, fv.name, fv.desc)
mv.visitInsn(ftype.getOpcode(IRETURN))
mv.visitMaxs(1, 1)
mv = cnode.visitMethod(ACC_PUBLIC, fv.name + "_$eq", "(" + f.desc + ")V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ftype.getOpcode(ILOAD), 1)
mv.visitFieldInsn(PUTFIELD, name, fv.name, fv.desc)
mv.visitInsn(RETURN)
mv.visitMaxs(width(ftype) + 1, width(ftype) + 1)
}
}
prevInfos+=t
t.supers.foreach {
s =>
val (name, desc) = seperateDesc(s._1)
val mv = cnode.visitMethod(ACC_PUBLIC, t.name.replace('/', '$') + "$$super$" + name, desc, null, null).asInstanceOf[MethodNode]
prevInfos.reverse.find(t => t.supers.contains(s._1)) match {
//each super goes to the one before
case Some(st) => writeStaticBridge(mv, name, st)
case None => writeBridge(mv, desc, INVOKESPECIAL, s._2, name, desc)
}
}
prevInfos += t
}
val methodSigs = MSet[String]()
traitInfos.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]
copy(m, mv)
mv.instructions = new InsnList()
mv.tryCatchBlocks.clear()
writeStaticBridge(mv, m.name, t)
methodSigs+=m.name+m.desc
traitInfos.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]
writeStaticBridge(mv, m.name, t)
methodSigs += m.name + m.desc
}
}
}
}
minit.visitInsn(RETURN)
minit.visitMaxs(1, 1)
define(cnode.name, createBytes(cnode, 0))
}
def seperateDesc(nameDesc:String) = {
def seperateDesc(nameDesc: String) = {
val n = nameDesc.indexOf('(')
(nameDesc.substring(0, n), nameDesc.substring(n))
}
def staticDesc(owner:String, desc:String) =
{
def staticDesc(owner: String, desc: String) = {
val descT = getMethodType(desc)
getMethodDescriptor(descT.getReturnType, getType("L"+owner+";")+:descT.getArgumentTypes : _*)
getMethodDescriptor(descT.getReturnType, getType("L" + owner + ";") +: descT.getArgumentTypes: _*)
}
def getSuper(minsn:MethodInsnNode, stack:StackAnalyser):Option[String] =
{
def getSuper(minsn: MethodInsnNode, stack: StackAnalyser): Option[String] = {
import StackAnalyser._
if(minsn.getOpcode != INVOKESPECIAL) return None
if (minsn.getOpcode != INVOKESPECIAL) return None
val oname = stack.owner.getInternalName
if(minsn.owner.equals(oname)) return None//private this
if (minsn.owner.equals(oname)) return None //private this
stack.peek(Type.getType(minsn.desc).getArgumentTypes.length) match {
case Load(This(o)) =>
case _ => return None//have to be invoked on this
case _ => return None //have to be invoked on this
}
getSuper(minsn.name+minsn.desc, oname)
getSuper(minsn.name + minsn.desc, oname)
}
def getSuper(nameDesc:String, cname:String) =
def getSuper(nameDesc: String, cname: String) =
getNodeInfo(cname).clazz.superClass match {
case None => None//no super class
case None => None //no super class
case Some(o) => o.publicmethods.get(nameDesc) match {
case None => None//no method with sig in super
case Some(m) => if(m.m.isAbstract)
None //abstract can't be called for supers
else
Some(m.owner)//congrats, that's a super
case None => None //no method with sig in super
case Some(m) => if (m.m.isAbstract)
None //abstract can't be called for supers
else
Some(m.owner) //congrats, that's a super
}
}
def registerJavaTrait(cnode:ClassNode)
{
if((cnode.access & ACC_INTERFACE) != 0)
throw new IllegalArgumentException("Cannot register java interface "+cnode.name+" as a multipart trait. Try register passThroughInterface")
if((cnode.access & ACC_ABSTRACT) != 0)
throw new IllegalArgumentException("Cannot register abstract class "+cnode.name+" as a multipart trait")
if(!cnode.innerClasses.isEmpty)
throw new IllegalArgumentException("Inner classes are not permitted for "+cnode.name+" as a multipart trait. Use scala")
val inode = new ClassNode()//impl node
inode.visit(V1_6, ACC_ABSTRACT|ACC_PUBLIC, cnode.name+"$class", null, "java/lang/Object", null)
def registerJavaTrait(cnode: ClassNode) {
if ((cnode.access & ACC_INTERFACE) != 0)
throw new IllegalArgumentException("Cannot register java interface " + cnode.name + " as a multipart trait. Try register passThroughInterface")
if ((cnode.access & ACC_ABSTRACT) != 0)
throw new IllegalArgumentException("Cannot register abstract class " + cnode.name + " as a multipart trait")
if (!cnode.innerClasses.isEmpty)
throw new IllegalArgumentException("Inner classes are not permitted for " + cnode.name + " as a multipart trait. Use scala")
val inode = new ClassNode() //impl node
inode.visit(V1_6, ACC_ABSTRACT | ACC_PUBLIC, cnode.name + "$class", null, "java/lang/Object", null)
inode.sourceFile = cnode.sourceFile
val fields = cnode.fields.map(f => (f.name, FieldMixin(f.name, f.desc, f.access))).toMap
val supers = MMap[String, String]()//nameDesc to super owner
val supers = MMap[String, String]() //nameDesc to super owner
val methods = MList[MethodNode]()
val methodSigs = cnode.methods.map(m => m.name+m.desc).toSet
val tnode = new ClassNode()//trait node (interface)
tnode.visit(V1_6, ACC_INTERFACE|ACC_ABSTRACT|ACC_PUBLIC, cnode.name, null, "java/lang/Object", Array(cnode.interfaces:_*))
def fname(name:String) = fields(name).accessName(cnode.name)
fields.values.foreach{ fnode =>
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name), "()"+fnode.desc, null, null)
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name)+"_$eq", "("+fnode.desc+")V", null, null)
val methodSigs = cnode.methods.map(m => m.name + m.desc).toSet
val tnode = new ClassNode() //trait node (interface)
tnode.visit(V1_6, ACC_INTERFACE | ACC_ABSTRACT | ACC_PUBLIC, cnode.name, null, "java/lang/Object", Array(cnode.interfaces: _*))
def fname(name: String) = fields(name).accessName(cnode.name)
fields.values.foreach {
fnode =>
tnode.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fname(fnode.name), "()" + fnode.desc, null, null)
tnode.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fname(fnode.name) + "_$eq", "(" + fnode.desc + ")V", null, null)
}
def superInsn(minsn:MethodInsnNode) =
{
val bridgeName = cnode.name.replace('/', '$')+"$$super$"+minsn.name
if(!supers.contains(minsn.name+minsn.desc))
{
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, bridgeName, minsn.desc, null, null)
supers.put(minsn.name+minsn.desc, minsn.owner)
def superInsn(minsn: MethodInsnNode) = {
val bridgeName = cnode.name.replace('/', '$') + "$$super$" + minsn.name
if (!supers.contains(minsn.name + minsn.desc)) {
tnode.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, bridgeName, minsn.desc, null, null)
supers.put(minsn.name + minsn.desc, minsn.owner)
}
new MethodInsnNode(INVOKEINTERFACE, cnode.name, bridgeName, minsn.desc)
}
def staticClone(mnode:MethodNode, name:String, access:Int) =
{
val mv = inode.visitMethod(access|ACC_STATIC, name,
staticDesc(cnode.name, mnode.desc),
null, Array(mnode.exceptions:_*)).asInstanceOf[MethodNode]
def staticClone(mnode: MethodNode, name: String, access: Int) = {
val mv = inode.visitMethod(access | ACC_STATIC, name,
staticDesc(cnode.name, mnode.desc),
null, Array(mnode.exceptions: _*)).asInstanceOf[MethodNode]
copy(mnode, mv)
mv
}
def staticTransform(mnode:MethodNode, base:MethodNode)
{
def staticTransform(mnode: MethodNode, base: MethodNode) {
val stack = new StackAnalyser(getType(cnode.name), base)
val insnList = mnode.instructions
var insn = insnList.getFirst
def replace(newinsn:AbstractInsnNode)
{
def replace(newinsn: AbstractInsnNode) {
insnList.insert(insn, newinsn)
insnList.remove(insn)
insn = newinsn
}
//transform
while(insn != null)
{
while (insn != null) {
insn match {
case finsn:FieldInsnNode => insn.getOpcode match
{
case GETFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name), "()"+finsn.desc))
case PUTFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name)+"_$eq", "("+finsn.desc+")V"))
case finsn: FieldInsnNode => insn.getOpcode match {
case GETFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name), "()" + finsn.desc))
case PUTFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name) + "_$eq", "(" + finsn.desc + ")V"))
case _ =>
}
case minsn:MethodInsnNode => insn.getOpcode match
{
case INVOKESPECIAL =>
if(getSuper(minsn, stack).isDefined)
case minsn: MethodInsnNode => insn.getOpcode match {
case INVOKESPECIAL =>
if (getSuper(minsn, stack).isDefined)
replace(superInsn(minsn))
case INVOKEVIRTUAL =>
if(minsn.owner.equals(cnode.name)) {
if(methodSigs.contains(minsn.name+minsn.desc))//call the interface method
if (minsn.owner.equals(cnode.name)) {
if (methodSigs.contains(minsn.name + minsn.desc)) //call the interface method
minsn.setOpcode(INVOKEINTERFACE)
else {//cast to parent class and call
else {
//cast to parent class and call
val mType = Type.getMethodType(minsn.desc)
val instanceEntry = stack.peek(width(mType.getArgumentTypes))
insnList.insert(instanceEntry.insn, new TypeInsnNode(CHECKCAST, cnode.superName))
@@ -478,150 +459,102 @@ object ASMMixinCompiler
insn = insn.getNext
}
}
def convertMethod(mnode:MethodNode)
{
if(mnode.name.equals("<clinit>"))
throw new IllegalArgumentException("Static initialisers are not permitted "+cnode.name+" as a multipart trait")
if(mnode.name.equals("<init>"))
{
if(!mnode.desc.equals("()V"))
throw new IllegalArgumentException("Constructor arguments are not permitted "+cnode.name+" as a multipart trait")
def convertMethod(mnode: MethodNode) {
if (mnode.name.equals("<clinit>"))
throw new IllegalArgumentException("Static initialisers are not permitted " + cnode.name + " as a multipart trait")
if (mnode.name.equals("<init>")) {
if (!mnode.desc.equals("()V"))
throw new IllegalArgumentException("Constructor arguments are not permitted " + cnode.name + " as a multipart trait")
val mv = staticClone(mnode, "$init$", ACC_PUBLIC)
def removeSuperConstructor()
{
def throwI = throw new IllegalArgumentException("Invalid constructor insn sequence "+cnode.name)
val insnList = mv.instructions
var insn = insnList.getFirst
var state = 0
while(insn != null && state < 2)
{
val next = insn.getNext
insn match {
case linsn:LabelNode =>
case linsn:LineNumberNode =>
case vinsn:VarInsnNode =>
if(state == 0)
{
if(vinsn.getOpcode == ALOAD && vinsn.`var` == 0)
{
state = 1
insnList.remove(insn)
}
else
throwI
}
else
throwI
case minsn:MethodInsnNode =>
if(state == 1)
{
if(minsn.getOpcode == INVOKESPECIAL && minsn.name.equals("<init>") &&
minsn.owner.equals(cnode.superName))
{
insnList.remove(insn)
state = 2
}
else
throwI
}
else
throwI
case _ => throwI
}
insn = next
}
if(state != 2)
throwI
def removeSuperConstructor() {
val insns = new InsnListSection
insns.add(new VarInsnNode(ALOAD, 0))
insns.add(new MethodInsnNode(INVOKESPECIAL, cnode.superName, "<init>", "()V"))
val minsns = new InsnListSection(mv.instructions)
val found = InsnComparator.matches(minsns, insns, Set[LabelNode]())
if(found == null)
throw new IllegalArgumentException("Invalid constructor insn sequence " + cnode.name + "\n" + minsns)
found.trim(Set[LabelNode]()).remove()
}
removeSuperConstructor()
staticTransform(mv, mnode)
return
}
if((mnode.access & ACC_PRIVATE) == 0)
{
val mv = tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, mnode.name, mnode.desc, null, Array(mnode.exceptions:_*))
methods+=mv.asInstanceOf[MethodNode]
if ((mnode.access & ACC_PRIVATE) == 0) {
val mv = tnode.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, mnode.name, mnode.desc, null, Array(mnode.exceptions: _*))
methods += mv.asInstanceOf[MethodNode]
}
//convert that method!
val access = if((mnode.access & ACC_PRIVATE) == 0) ACC_PUBLIC else ACC_PRIVATE
val access = if ((mnode.access & ACC_PRIVATE) == 0) ACC_PUBLIC else ACC_PRIVATE
val mv = staticClone(mnode, mnode.name, access)
staticTransform(mv, mnode)
}
cnode.methods.foreach(convertMethod)
define(inode.name, createBytes(inode, 0))
define(tnode.name, createBytes(tnode, 0))
mixinMap.put(tnode.name, MixinInfo(tnode.name, cnode.superName,
fields.values.toSeq,
methods.toSeq, supers.toMap))
mixinMap.put(tnode.name, MixinInfo(tnode.name, cnode.superName,
fields.values.toSeq,
methods.toSeq, supers.toMap))
}
def registerScalaTrait(cnode:ClassNode)
{
for(i <- cnode.interfaces)
{
def registerScalaTrait(cnode: ClassNode) {
for (i <- cnode.interfaces) {
val inode = classNode(i)
if(isScala(inode) && isTrait(inode))
throw new IllegalArgumentException("Multipart trait "+cnode.name+" cannot extend other traits")
if (isScala(inode) && isTrait(inode))
throw new IllegalArgumentException("Multipart trait " + cnode.name + " cannot extend other traits")
}
val fieldAccessors = MMap[String, MethodSymbol]()
val fields = MList[MethodSymbol]()
val methods = MList[MethodNode]()
val supers = MMap[String, String]()
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
val csym:ClassSymbol = sig.evalT(0)
for(i <- 0 until sig.table.length)
{
val csym: ClassSymbol = sig.evalT(0)
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)){}
else if(sym.isAccessor)
{
if (e.id == 8) {//method
val sym: MethodSymbol = sig.evalT(i)
if (sym.isParam || !sym.owner.equals(csym)) {}
else if (sym.isAccessor) {
fieldAccessors.put(sym.name, sym)
}
else if(sym.isMethod)
{
else if (sym.isMethod) {
val desc = sym.jDesc(sig)
if(sym.name.contains("super$"))
{
if (sym.name.contains("super$")) {
val name = sym.name.substring(6)
supers.put(name+desc, getSuper(name+desc, cnode.name).get)
supers.put(name + desc, getSuper(name + desc, cnode.name).get)
}
else if(!sym.name.equals("$init$") && !sym.isPrivate)
{
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 multipart trait: "+map+" found in scala signature but not in class file. Most likely an obfuscation issue.")
methods+=method
if (method == null)
throw new RuntimeException("Unable to add multipart trait: " + map + " found in scala signature but not in class file. Most likely an obfuscation issue.")
methods += method
}
}
else
{
fields+=sym
else {
fields += sym
}
}
}
mixinMap.put(cnode.name, MixinInfo(cnode.name, csym.jParent(sig),
fields.map(sym => FieldMixin(sym.name.trim, getReturnType(sym.jDesc(sig)).getDescriptor,
if(fieldAccessors(sym.name.trim).isPrivate) ACC_PRIVATE else ACC_PUBLIC)),
methods, supers.toMap))
mixinMap.put(cnode.name, MixinInfo(cnode.name, csym.jParent(sig),
fields.map(sym => FieldMixin(sym.name.trim, getReturnType(sym.jDesc(sig)).getDescriptor,
if (fieldAccessors(sym.name.trim).isPrivate) ACC_PRIVATE else ACC_PUBLIC)),
methods, supers.toMap))
}
}
@@ -189,7 +189,7 @@ object ASMMixinFactory extends IMultipartFactory
val inode = classNode(s_interface)
if(inode == null) {
System.out.println("Unable to generate pass through trait for: "+s_interface+" class not found.")
logger.error("Unable to generate pass through trait for: "+s_interface+" class not found.")
return null
}
if((inode.access&ACC_INTERFACE) == 0) throw new IllegalArgumentException(s_interface+" is not an interface.")
@@ -24,7 +24,7 @@ object MCPCCompatModule
}
} catch {
case e:Exception =>
System.err.println("Failed to integrate MCPC placement hooks")
MultipartProxy.logger.error("Failed to integrate MCPC placement hooks")
e.printStackTrace()
}
}
@@ -8,35 +8,29 @@ import cpw.mods.fml.common.Mod.EventHandler
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent
import codechicken.multipart.MultiPartRegistry
@Mod(modid = "ForgeMultipart", acceptedMinecraftVersions = "[1.7.2]",
modLanguage="scala")
@Mod(modid = "ForgeMultipart", acceptedMinecraftVersions = "[1.7.2]", modLanguage = "scala")
object MultipartMod
{
@EventHandler
def preInit(event:FMLPreInitializationEvent)
{
MultipartProxy.preInit(event.getModConfigurationDirectory)
def preInit(event: FMLPreInitializationEvent) {
MultipartProxy.preInit(event.getModConfigurationDirectory, event.getModLog)
}
@EventHandler
def init(event:FMLInitializationEvent)
{
def init(event: FMLInitializationEvent) {
MultipartProxy.init()
}
@EventHandler
def postInit(event:FMLPostInitializationEvent)
{
if(MultiPartRegistry.required)
{
def postInit(event: FMLPostInitializationEvent) {
if (MultiPartRegistry.required) {
MultiPartRegistry.postInit()
MultipartProxy.postInit()
}
}
@EventHandler
def beforeServerStart(event:FMLServerAboutToStartEvent)
{
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MultiPartRegistry.beforeServerStart()
}
}
+18 -21
View File
@@ -6,7 +6,6 @@ import cpw.mods.fml.client.registry.ClientRegistry
import net.minecraft.tileentity.TileEntity
import codechicken.lib.config.ConfigFile
import java.io.File
import codechicken.multipart.handler.MultipartProxy._
import net.minecraftforge.common.MinecraftForge
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
@@ -17,11 +16,16 @@ import codechicken.lib.world.WorldExtensionManager
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.registry.GameRegistry
import net.minecraft.block.Block
import org.apache.logging.log4j.Logger
class MultipartProxy_serverImpl
{
def preInit(cfgdir:File)
{
var block:BlockMultipart = _
var config:ConfigFile = _
var logger:Logger = _
def preInit(cfgdir: File, logger:Logger) {
this.logger = logger
config = new ConfigFile(new File(cfgdir, "multipart.cfg"))
.setComment("Multipart API config file")
@@ -36,14 +40,13 @@ class MultipartProxy_serverImpl
MultipartGenerator.registerTrait("codechicken.multipart.IRedstonePart", "codechicken.multipart.scalatraits.TRedstoneTile")
MultipartGenerator.registerTrait("codechicken.multipart.IRandomDisplayTick", "codechicken.multipart.scalatraits.TRandomDisplayTickTile", null)
MultipartGenerator.registerTrait("codechicken.multipart.INeighborTileChange", null, "codechicken.multipart.scalatraits.TTileChangeTile")
MultipartSaveLoad.hookLoader()
}
def init(){}
def init() {}
def postInit()
{
def postInit() {
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
@@ -53,18 +56,16 @@ class MultipartProxy_serverImpl
MultipartCompatiblity.load()
}
def onTileClassBuilt(t:Class[_ <: TileEntity])
{
MultipartSaveLoad.registerTileClass(t)
def onTileClassBuilt(t: Class[_ <: TileEntity]) {
MultipartSaveLoad.registerTileClass(t)
}
}
class MultipartProxy_clientImpl extends MultipartProxy_serverImpl
{
@SideOnly(Side.CLIENT)
override def postInit()
{
override def postInit() {
super.postInit()
RenderingRegistry.registerBlockHandler(MultipartRenderer)
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
@@ -73,10 +74,9 @@ class MultipartProxy_clientImpl extends MultipartProxy_serverImpl
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
ClientRegistry.registerKeyBinding(ControlKeyHandler)
}
@SideOnly(Side.CLIENT)
override def onTileClassBuilt(t:Class[_ <: TileEntity])
{
override def onTileClassBuilt(t: Class[_ <: TileEntity]) {
super.onTileClassBuilt(t)
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
}
@@ -84,9 +84,6 @@ class MultipartProxy_clientImpl extends MultipartProxy_serverImpl
object MultipartProxy extends MultipartProxy_clientImpl
{
var block:BlockMultipart = _
var config:ConfigFile = _
def indexInChunk(cc:ChunkCoordIntPair, i:Int) = new BlockCoord(cc.chunkXPos<<4|i&0xF, (i>>8)&0xFF, cc.chunkZPos<<4|(i&0xF0)>>4)
def indexInChunk(pos:BlockCoord) = pos.x&0xF|pos.y<<8|(pos.z&0xF)<<4
def indexInChunk(cc: ChunkCoordIntPair, i: Int) = new BlockCoord(cc.chunkXPos << 4 | i & 0xF, (i >> 8) & 0xFF, cc.chunkZPos << 4 | (i & 0xF0) >> 4)
def indexInChunk(pos: BlockCoord) = pos.x & 0xF | pos.y << 8 | (pos.z & 0xF) << 4
}
+8
View File
@@ -0,0 +1,8 @@
package codechicken
import codechicken.multipart.handler.MultipartProxy
package object multipart
{
def logger = MultipartProxy.logger
}