[ci skip] spotlessApply with the new settings

This commit is contained in:
Jason Mitchell committed 2023-01-29 10:24:12 -08:00
1 parent f02b6bae19
commit 6b18eca613
89 files changed
+8520 -7190

No files matched your search

@@ -1,13 +1,5 @@
package codechicken.core.launch;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import cpw.mods.fml.common.versioning.ComparableVersion;
import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.WindowAdapter;
@@ -28,23 +20,37 @@ import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import net.minecraft.launchwrapper.LaunchClassLoader;
import sun.misc.URLClassPath;
import sun.net.util.URLUtil;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import cpw.mods.fml.common.versioning.ComparableVersion;
import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
/**
* For autodownloading stuff.
* This is really unoriginal, mostly ripped off FML, credits to cpw.
* For autodownloading stuff. This is really unoriginal, mostly ripped off FML, credits to cpw.
*/
public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
private static ByteBuffer downloadBuffer = ByteBuffer.allocateDirect(1 << 23);
private static final String owner = "CB's DepLoader";
private static DepLoadInst inst;
public interface IDownloadDisplay {
void resetProgress(int sizeGuess);
void setPokeThread(Thread currentThread);
@@ -62,6 +68,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
@SuppressWarnings("serial")
public static class Downloader extends JOptionPane implements IDownloadDisplay {
private JDialog container;
private JLabel currentActivity;
private JProgressBar progress;
@@ -71,12 +78,13 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
private Box makeProgressPanel() {
Box box = Box.createVerticalBox();
box.add(Box.createRigidArea(new Dimension(0, 10)));
JLabel welcomeLabel = new JLabel("<html><b><font size='+1'>" + owner
+ " is setting up your minecraft environment</font></b></html>");
JLabel welcomeLabel = new JLabel(
"<html><b><font size='+1'>" + owner
+ " is setting up your minecraft environment</font></b></html>");
box.add(welcomeLabel);
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
welcomeLabel =
new JLabel("<html>Please wait, " + owner + " has some tasks to do before you can play</html>");
welcomeLabel = new JLabel(
"<html>Please wait, " + owner + " has some tasks to do before you can play</html>");
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
box.add(welcomeLabel);
box.add(Box.createRigidArea(new Dimension(0, 10)));
@@ -96,8 +104,9 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
setMessageType(JOptionPane.INFORMATION_MESSAGE);
setMessage(makeProgressPanel());
setOptions(new Object[] {"Stop"});
setOptions(new Object[] { "Stop" });
addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == Downloader.this && evt.getPropertyName() == VALUE_PROPERTY) {
@@ -115,6 +124,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
container.setVisible(true);
container.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
container.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
requestClose(
@@ -168,21 +178,26 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
JEditorPane ep = new JEditorPane(
"text/html",
"<html>" + owner
+ " was unable to download required library " + name
+ " was unable to download required library "
+ name
+ "<br>Check your internet connection and try restarting or download it manually from"
+ "<br><a href=\""
+ url + "\">" + url + "</a> and put it in your mods folder" + "</html>");
+ url
+ "\">"
+ url
+ "</a> and put it in your mods folder"
+ "</html>");
ep.setEditable(false);
ep.setOpaque(false);
ep.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent event) {
try {
if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
Desktop.getDesktop().browse(event.getURL().toURI());
} catch (Exception e) {
}
} catch (Exception e) {}
}
});
@@ -191,6 +206,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
public static class DummyDownloader implements IDownloadDisplay {
@Override
public void resetProgress(int sizeGuess) {}
@@ -218,6 +234,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
public static class VersionedFile {
public final Pattern pattern;
public final String filename;
public final ComparableVersion version;
@@ -242,6 +259,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
public static class Dependency {
public String url;
public VersionedFile file;
@@ -259,6 +277,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
public static class DepLoadInst {
private File modsDir;
private File v_modsDir;
private IDownloadDisplay downloadMonitor;
@@ -310,7 +329,8 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
if (!mod.delete()) {
mod.deleteOnExit();
String msg = owner + " was unable to delete file " + mod.getPath()
String msg = owner + " was unable to delete file "
+ mod.getPath()
+ " the game will now try to delete it on exit. If this dialog appears again, delete it manually.";
System.err.println(msg);
if (!GraphicsEnvironment.isHeadless())
@@ -350,10 +370,10 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
private void download(InputStream is, int sizeGuess, File target) throws Exception {
if (sizeGuess > downloadBuffer.capacity())
throw new Exception(String.format(
"The file %s is too large to be downloaded by " + owner + " - the download is invalid",
target.getName()));
if (sizeGuess > downloadBuffer.capacity()) throw new Exception(
String.format(
"The file %s is too large to be downloaded by " + owner + " - the download is invalid",
target.getName()));
downloadBuffer.clear();
@@ -384,20 +404,20 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
try {
/*String cksum = generateChecksum(downloadBuffer);
if (cksum.equals(validationHash))
{*/
/*
* String cksum = generateChecksum(downloadBuffer); if (cksum.equals(validationHash)) {
*/
if (!target.exists()) target.createNewFile();
downloadBuffer.position(0);
FileOutputStream fos = new FileOutputStream(target);
fos.getChannel().write(downloadBuffer);
fos.close();
/*}
else
{
throw new RuntimeException(String.format("The downloaded file %s has an invalid checksum %s (expecting %s). The download did not succeed correctly and the file has been deleted. Please try launching again.", target.getName(), cksum, validationHash));
}*/
/*
* } else { throw new RuntimeException(String.
* format("The downloaded file %s has an invalid checksum %s (expecting %s). The download did not succeed correctly and the file has been deleted. Please try launching again."
* , target.getName(), cksum, validationHash)); }
*/
} catch (Exception e) {
throw e;
}
@@ -424,8 +444,12 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
return null;
}
if (cmp > 0) {
System.err.println("Warning: version of " + dep.file.name + ", " + vfile.version
+ " is newer than request " + dep.file.version);
System.err.println(
"Warning: version of " + dep.file.name
+ ", "
+ vfile.version
+ " is newer than request "
+ dep.file.version);
return f.getName();
}
return f.getName(); // found dependency
@@ -512,9 +536,8 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
}
private void loadJson(JsonObject node) throws IOException {
boolean obfuscated =
((LaunchClassLoader) DepLoader.class.getClassLoader()).getClassBytes("net.minecraft.world.World")
== null;
boolean obfuscated = ((LaunchClassLoader) DepLoader.class.getClassLoader())
.getClassBytes("net.minecraft.world.World") == null;
String testClass = node.get("class").getAsString();
if (DepLoader.class.getResource("/" + testClass.replace('.', '/') + ".class") != null) return;
@@ -527,8 +550,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
Pattern pattern = null;
try {
if (node.has("pattern"))
pattern = Pattern.compile(node.get("pattern").getAsString());
if (node.has("pattern")) pattern = Pattern.compile(node.get("pattern").getAsString());
} catch (PatternSyntaxException e) {
System.err.println("Invalid filename pattern: " + node.get("pattern"));
e.printStackTrace();
@@ -8,132 +8,168 @@ import cpw.mods.fml.relauncher.Side
import net.minecraft.item.ItemStack
import codechicken.lib.vec.{Cuboid6, Vector3}
import net.minecraft.entity.player.EntityPlayer
import codechicken.lib.render.{CCRenderPipeline, ColourMultiplier, CCRenderState}
import codechicken.lib.render.{
CCRenderPipeline,
ColourMultiplier,
CCRenderState
}
import codechicken.microblock.handler.MicroblockProxy
import net.minecraft.entity.Entity
import codechicken.lib.render.uv.{UVTransformation, MultiIconTransformation}
import cpw.mods.fml.common.registry.{GameRegistry, GameData}
object MaterialRenderHelper
{
private var pass = 0
private var builder:CCRenderPipeline#PipelineBuilder = _
object MaterialRenderHelper {
private var pass = 0
private var builder: CCRenderPipeline#PipelineBuilder = _
def start(pos:Vector3, pass:Int, uvt:UVTransformation) = {
this.pass = pass
builder = CCRenderState.pipeline.builder()
builder.add(pos.translation()).add(uvt)
this
}
def start(pos: Vector3, pass: Int, uvt: UVTransformation) = {
this.pass = pass
builder = CCRenderState.pipeline.builder()
builder.add(pos.translation()).add(uvt)
this
}
def blockColour(colour:Int) = {
builder.add(ColourMultiplier.instance(colour))
this
}
def blockColour(colour: Int) = {
builder.add(ColourMultiplier.instance(colour))
this
}
def lighting() = {
if(pass != -1)
builder.add(CCRenderState.lightMatrix)
this
}
def lighting() = {
if (pass != -1)
builder.add(CCRenderState.lightMatrix)
this
}
def render() = builder.render()
def render() = builder.render()
}
/**
* Standard micro material class suitable for most blocks.
*/
class BlockMicroMaterial(val block:Block, val meta:Int = 0) extends IMicroMaterial
{
val blockKey = Block.blockRegistry.getNameForObject(block)
/** Standard micro material class suitable for most blocks.
*/
class BlockMicroMaterial(val block: Block, val meta: Int = 0)
extends IMicroMaterial {
val blockKey = Block.blockRegistry.getNameForObject(block)
@SideOnly(Side.CLIENT)
var icont:MultiIconTransformation = _
@SideOnly(Side.CLIENT)
var icont: MultiIconTransformation = _
@SideOnly(Side.CLIENT)
override def loadIcons() {
def safeIcon(block:Block, side:Int):IIcon = {
try {
return MicroblockProxy.renderBlocks.getIconSafe(block.getIcon(side, meta))
}
catch {
case e:Exception =>
}
return MicroblockProxy.renderBlocks.getIconSafe(null)
}
val iblock = Block.getBlockFromName(blockKey)//reacquire block instance incase a mod replcaed it
icont = new MultiIconTransformation(Array.tabulate(6)(side => safeIcon(iblock, side)):_*)
@SideOnly(Side.CLIENT)
override def loadIcons() {
def safeIcon(block: Block, side: Int): IIcon = {
try {
return MicroblockProxy.renderBlocks.getIconSafe(
block.getIcon(side, meta)
)
} catch {
case e: Exception =>
}
return MicroblockProxy.renderBlocks.getIconSafe(null)
}
def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
}
val iblock = Block.getBlockFromName(
blockKey
) // reacquire block instance incase a mod replcaed it
icont = new MultiIconTransformation(
Array.tabulate(6)(side => safeIcon(iblock, side)): _*
)
}
def getColour(pass:Int) = {
if(pass == -1)
block.getBlockColor<<8|0xFF
else {
val pos = CCRenderState.lightMatrix.pos
block.colorMultiplier(CCRenderState.lightMatrix.access, pos.x, pos.y, pos.z)<<8|0xFF
}
}
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
MaterialRenderHelper
.start(pos, pass, icont)
.blockColour(getColour(pass))
.lighting()
.render()
}
override def canRenderInPass(pass:Int) = block.canRenderInPass(pass)
@SideOnly(Side.CLIENT)
def getBreakingIcon(side:Int) = block.getIcon(side, meta)
def getItem = new ItemStack(block, 1, meta)
def getLocalizedName = getItem.getDisplayName
def getStrength(player:EntityPlayer) = {
var hardness = 30F
try {
hardness = block.getBlockHardness(null, 0, 0, 0)
} catch {
case e:Exception =>
}
player.getBreakSpeed(block, false, meta%16, 0, -1, 0) / hardness
def getColour(pass: Int) = {
if (pass == -1)
block.getBlockColor << 8 | 0xff
else {
val pos = CCRenderState.lightMatrix.pos
block.colorMultiplier(
CCRenderState.lightMatrix.access,
pos.x,
pos.y,
pos.z
) << 8 | 0xff
}
def isTransparent = !block.isOpaqueCube
def getLightValue = block.getLightValue
def toolClasses = Seq("axe", "pickaxe", "shovel")
// meta > 15 will throw ArrayIndexOutOfBoundsException
def getCutterStrength = block.getHarvestLevel(meta % 16)
def getSound = block.stepSound
}
def explosionResistance(entity:Entity):Float = block.getExplosionResistance(entity)
override def canRenderInPass(pass: Int) = block.canRenderInPass(pass)
@SideOnly(Side.CLIENT)
def getBreakingIcon(side: Int) = block.getIcon(side, meta)
def getItem = new ItemStack(block, 1, meta)
def getLocalizedName = getItem.getDisplayName
def getStrength(player: EntityPlayer) = {
var hardness = 30f
try {
hardness = block.getBlockHardness(null, 0, 0, 0)
} catch {
case e: Exception =>
}
player.getBreakSpeed(block, false, meta % 16, 0, -1, 0) / hardness
}
def isTransparent = !block.isOpaqueCube
def getLightValue = block.getLightValue
def toolClasses = Seq("axe", "pickaxe", "shovel")
// meta > 15 will throw ArrayIndexOutOfBoundsException
def getCutterStrength = block.getHarvestLevel(meta % 16)
def getSound = block.stepSound
def explosionResistance(entity: Entity): Float =
block.getExplosionResistance(entity)
}
/**
* Utility functions for cleaner registry code
*/
object BlockMicroMaterial
{
def oldKey(block:Block) = block.getUnlocalizedName
def materialKey(block:Block) = Block.blockRegistry.getNameForObject(block)
def materialKey(name:String, meta:Int) = name+(if(meta > 0) "_"+meta else "")
def materialKey(block:Block, meta:Int):String = materialKey(materialKey(block), meta)
/** Utility functions for cleaner registry code
*/
object BlockMicroMaterial {
def oldKey(block: Block) = block.getUnlocalizedName
def materialKey(block: Block) = Block.blockRegistry.getNameForObject(block)
def materialKey(name: String, meta: Int) =
name + (if (meta > 0) "_" + meta else "")
def materialKey(block: Block, meta: Int): String =
materialKey(materialKey(block), meta)
def createAndRegister(block:Block, meta:Int, name:String) =
MicroMaterialRegistry.registerMaterial(new BlockMicroMaterial(block, meta), materialKey(name, meta))
def createAndRegister(block: Block, meta: Int, name: String) =
MicroMaterialRegistry.registerMaterial(
new BlockMicroMaterial(block, meta),
materialKey(name, meta)
)
def createAndRegister(block:Block, meta:Int, name:String, oldName:String) {
MicroMaterialRegistry.remapName(materialKey(oldName, meta), materialKey(name, meta))
createAndRegister(block, meta, name)
}
def createAndRegister(
block: Block,
meta: Int,
name: String,
oldName: String
) {
MicroMaterialRegistry.remapName(
materialKey(oldName, meta),
materialKey(name, meta)
)
createAndRegister(block, meta, name)
}
def createAndRegister(block:Block, meta:Int = 0):Unit = createAndRegister(block, Seq(meta))
def createAndRegister(block:Block, meta:Seq[Int]):Unit = createAndRegister(block, meta, materialKey(block), oldKey(block))
def createAndRegister(block:Block, meta:Seq[Int], oldName:String):Unit = createAndRegister(block, 0, materialKey(block), oldName)
def createAndRegister(block: Block, meta: Int = 0): Unit =
createAndRegister(block, Seq(meta))
def createAndRegister(block: Block, meta: Seq[Int]): Unit =
createAndRegister(block, meta, materialKey(block), oldKey(block))
def createAndRegister(block: Block, meta: Seq[Int], oldName: String): Unit =
createAndRegister(block, 0, materialKey(block), oldName)
def createAndRegister(block:Block, meta:Seq[Int], name:String, oldName:String):Unit =
meta.foreach(m => createAndRegister(block, m, name, oldName))
def createAndRegister(
block: Block,
meta: Seq[Int],
name: String,
oldName: String
): Unit =
meta.foreach(m => createAndRegister(block, m, name, oldName))
}
@@ -16,133 +16,158 @@ import BlockMicroMaterial.materialKey
import java.lang.{Iterable => JIterable}
import scala.collection.JavaConversions._
object ConfigContent
{
private val nameMap = MMap[String, Seq[Int]]()
object ConfigContent {
private val nameMap = MMap[String, Seq[Int]]()
def parse(cfgDir: File) {
val cfgFile = new File(cfgDir, "microblocks.cfg")
def parse(cfgDir: File) {
val cfgFile = new File(cfgDir, "microblocks.cfg")
try {
if (!cfgFile.exists())
generateDefault(cfgFile)
else
loadLines(cfgFile)
} catch {
case e: IOException => logger.error("Error parsing config", e)
}
}
def generateDefault(cfgFile: File) {
val writer = new PrintWriter(cfgFile)
writer.println(
"#Configuration file for adding microblock materials for aesthetic blocks added by mods"
)
writer.println("#Each line needs to be of the form <name>:<meta>")
writer.println(
"#<name> is the unlocalised name or registry key of the block/item enclosed in quotes. NEI can help you find these"
)
writer.println(
"#<meta> may be ommitted, in which case it defaults to 0, otherwise it can be a number, a comma separated list of numbers, or a dash separated range"
)
writer.println(
"#Ex. \"dirt\" \"minecraft:planks\":3 \"iron_ore\":1,2,3,5 \"ThermalFoundation:Storage\":0-15"
)
writer.close()
}
def loadLine(line: String) {
if (line.startsWith("#") || line.length < 3)
return
if (line.charAt(0) != '\"')
throw new IllegalArgumentException("Line must begin with a quote")
val q2 = line.indexOf('\"', 1)
if (q2 < 0)
throw new IllegalArgumentException("Unmatched quotes")
var name = line.substring(1, q2)
if (!name.contains('.') && !name.contains(':'))
name = "minecraft:" + name
var meta = Seq(0)
if (line.length > q2 + 1) {
if (line.charAt(q2 + 1) != ':')
throw new IllegalArgumentException(
"Name must be followed by a colon separator"
)
meta = line.substring(q2 + 2).split(",").flatMap { s =>
if (s.contains("-")) {
val split2 = s.split("-")
if (split2.length != 2)
throw new IllegalArgumentException("Invalid - separated range")
split2(0).toInt to split2(1).toInt
} else {
Seq(s.toInt)
}
}
}
nameMap.put(name, meta)
}
def loadLines(cfgFile: File) {
val reader = new BufferedReader(new FileReader(cfgFile))
var s: String = null
do {
s = reader.readLine
if (s != null) {
try {
if (!cfgFile.exists())
generateDefault(cfgFile)
else
loadLines(cfgFile)
loadLine(s)
} catch {
case e: Exception =>
logger.error("Invalid line in microblocks.cfg: " + s)
logger.error(e.getMessage)
}
catch {
case e: IOException => logger.error("Error parsing config", e)
}
} while (s != null)
reader.close()
}
def load() {
for (block <- Block.blockRegistry.asInstanceOf[JIterable[Block]]) {
val metas = Seq(
block.getUnlocalizedName,
Block.blockRegistry.getNameForObject(block)
).flatMap(nameMap.remove).flatten
metas.foreach { m =>
try {
createAndRegister(block, m)
} catch {
case e: IllegalStateException =>
logger.error(
"Unable to register micro material: " +
materialKey(block, m) + "\n\t" + e.getMessage
)
case e: Exception =>
logger.error(
"Unable to register micro material: " + materialKey(block, m),
e
)
}
}
}
def generateDefault(cfgFile: File) {
val writer = new PrintWriter(cfgFile)
writer.println("#Configuration file for adding microblock materials for aesthetic blocks added by mods")
writer.println("#Each line needs to be of the form <name>:<meta>")
writer.println("#<name> is the unlocalised name or registry key of the block/item enclosed in quotes. NEI can help you find these")
writer.println("#<meta> may be ommitted, in which case it defaults to 0, otherwise it can be a number, a comma separated list of numbers, or a dash separated range")
writer.println("#Ex. \"dirt\" \"minecraft:planks\":3 \"iron_ore\":1,2,3,5 \"ThermalFoundation:Storage\":0-15")
writer.close()
}
nameMap.foreach(e =>
logger.warn(
"Unable to add micro material for block with unlocalised name " + e._1 + " as it doesn't exist"
)
)
}
def loadLine(line: String) {
if (line.startsWith("#") || line.length < 3)
return
def handleIMC(messages: Seq[IMCMessage]) {
messages.filter(_.key == "microMaterial").foreach { msg =>
def error(s: String) {
logger.error(
"Invalid microblock IMC message from " + msg.getSender + ": " + s
)
}
if(line.charAt(0) != '\"')
throw new IllegalArgumentException("Line must begin with a quote")
val q2 = line.indexOf('\"', 1)
if(q2 < 0)
throw new IllegalArgumentException("Unmatched quotes")
var name = line.substring(1, q2)
if(!name.contains('.') && !name.contains(':'))
name = "minecraft:"+name
var meta = Seq(0)
if(line.length > q2+1) {
if(line.charAt(q2+1) != ':')
throw new IllegalArgumentException("Name must be followed by a colon separator")
meta = line.substring(q2+2).split(",").flatMap{s =>
if (s.contains("-")) {
val split2 = s.split("-")
if (split2.length != 2)
throw new IllegalArgumentException("Invalid - separated range")
split2(0).toInt to split2(1).toInt
}
else {
Seq(s.toInt)
}
}
if (msg.getMessageType != classOf[ItemStack])
error("value is not an instanceof ItemStack")
else {
val stack = msg.getItemStackValue
if (!Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
error("Invalid Block: " + stack.getItem)
else if (stack.getItemDamage < 0 || stack.getItemDamage >= 16)
error("Invalid metadata: " + stack.getItemDamage)
else {
try {
createAndRegister(
Block.getBlockFromItem(stack.getItem),
stack.getItemDamage
)
} catch {
case e: IllegalStateException =>
error(
"Unable to register micro material: " +
materialKey(
Block.getBlockFromItem(stack.getItem),
stack.getItemDamage
) + "\n\t" + e.getMessage
)
}
}
nameMap.put(name, meta)
}
}
def loadLines(cfgFile: File) {
val reader = new BufferedReader(new FileReader(cfgFile))
var s: String = null
do {
s = reader.readLine
if (s != null) {
try {
loadLine(s)
}
catch {
case e: Exception =>
logger.error("Invalid line in microblocks.cfg: " + s)
logger.error(e.getMessage)
}
}
}
while (s != null)
reader.close()
}
def load() {
for(block <- Block.blockRegistry.asInstanceOf[JIterable[Block]]) {
val metas = Seq(block.getUnlocalizedName, Block.blockRegistry.getNameForObject(block)).flatMap(nameMap.remove).flatten
metas.foreach{m =>
try {
createAndRegister(block, m)
}
catch {
case e: IllegalStateException => logger.error("Unable to register micro material: " +
materialKey(block, m) + "\n\t" + e.getMessage)
case e: Exception =>
logger.error("Unable to register micro material: " + materialKey(block, m), e)
}
}
}
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) {
logger.error("Invalid microblock IMC message from " + msg.getSender + ": " + s)
}
if (msg.getMessageType != classOf[ItemStack])
error("value is not an instanceof ItemStack")
else {
val stack = msg.getItemStackValue
if (!Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
error("Invalid Block: " + stack.getItem)
else if (stack.getItemDamage < 0 || stack.getItemDamage >= 16)
error("Invalid metadata: " + stack.getItemDamage)
else {
try {
createAndRegister(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)
}
catch {
case e: IllegalStateException => error("Unable to register micro material: " +
materialKey(Block.getBlockFromItem(stack.getItem), stack.getItemDamage) + "\n\t" + e.getMessage)
}
}
}
}
}
}
}
}
@@ -5,52 +5,48 @@ import codechicken.lib.vec.Scale
import codechicken.lib.vec.Vector3
import Vector3._
object CornerPlacement extends PlacementProperties
{
def microClass = CornerMicroClass
def placementGrid = CornerPlacementGrid
def opposite(slot:Int, side:Int) = ((slot-7)^(1<<(side>>1)))+7
object CornerPlacement extends PlacementProperties {
def microClass = CornerMicroClass
def placementGrid = CornerPlacementGrid
def opposite(slot: Int, side: Int) = ((slot - 7) ^ (1 << (side >> 1))) + 7
}
object CornerMicroClass extends CommonMicroClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 8)
{
val rx = if((s&4) != 0) -1 else 1
val ry = if((s&1) != 0) -1 else 1
val rz = if((s&2) != 0) -1 else 1
val transform = new Scale(new Vector3(rx, ry, rz)).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, d, d).apply(transform)
}
object CornerMicroClass extends CommonMicroClass {
var aBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 8) {
val rx = if ((s & 4) != 0) -1 else 1
val ry = if ((s & 1) != 0) -1 else 1
val rz = if ((s & 2) != 0) -1 else 1
val transform = new Scale(new Vector3(rx, ry, rz)).at(center)
for (t <- 1 until 8) {
val d = t / 8d
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, d, d, d).apply(transform)
}
def getName = "mcr_cnr"
}
def baseTrait = classOf[CornerMicroblock]
def clientTrait = classOf[CommonMicroblockClient]
override def itemSlot = 7
def placementProperties = CornerPlacement
def getName = "mcr_cnr"
def getResistanceFactor = 1
def baseTrait = classOf[CornerMicroblock]
def clientTrait = classOf[CommonMicroblockClient]
override def itemSlot = 7
def placementProperties = CornerPlacement
def getResistanceFactor = 1
}
trait CornerMicroblock extends CommonMicroblock
{
override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-7)).toByte
def microClass = CornerMicroClass
def getBounds = CornerMicroClass.aBounds(shape)
override def getSlot = getShape+7
}
trait CornerMicroblock extends CommonMicroblock {
override def setShape(size: Int, slot: Int) = shape =
(size << 4 | (slot - 7)).toByte
def microClass = CornerMicroClass
def getBounds = CornerMicroClass.aBounds(shape)
override def getSlot = getShape + 7
}
@@ -4,57 +4,55 @@ import net.minecraft.init.Blocks._
import BlockMicroMaterial._
import MicroMaterialRegistry._
object DefaultContent
{
def load()
{
FaceMicroClass.register(0)
HollowMicroClass.register(1)
CornerMicroClass.register(2)
EdgeMicroClass.register(3)
PostMicroClass.register()
createAndRegister(stone)
createAndRegister(dirt, 0 to 2)
createAndRegister(cobblestone)
createAndRegister(planks, 0 to 5)
createAndRegister(log, 0 to 3)
createAndRegister(log2, 0 to 1, "tile.log2")
createAndRegister(leaves, 0 to 3)
createAndRegister(leaves2, 0 to 1, "tile.leaves2")
createAndRegister(sponge)
createAndRegister(glass)
createAndRegister(lapis_block)
createAndRegister(sandstone, 0 to 2)
createAndRegister(wool, 0 to 15)
createAndRegister(gold_block)
createAndRegister(iron_block)
createAndRegister(brick_block)
createAndRegister(bookshelf)
createAndRegister(mossy_cobblestone)
createAndRegister(obsidian)
createAndRegister(diamond_block)
createAndRegister(ice)
createAndRegister(snow)
createAndRegister(clay)
createAndRegister(netherrack)
createAndRegister(soul_sand)
createAndRegister(glowstone)
createAndRegister(stonebrick, 0 to 3)
createAndRegister(nether_brick)
createAndRegister(end_stone)
createAndRegister(emerald_block)
createAndRegister(redstone_block)
createAndRegister(quartz_block)
createAndRegister(stained_hardened_clay, 0 to 15)
createAndRegister(hardened_clay)
createAndRegister(coal_block)
createAndRegister(packed_ice)
createAndRegister(stained_glass, 0 to 15)
object DefaultContent {
def load() {
FaceMicroClass.register(0)
HollowMicroClass.register(1)
CornerMicroClass.register(2)
EdgeMicroClass.register(3)
PostMicroClass.register()
MicroMaterialRegistry.remapName(oldKey(grass), materialKey(grass))
registerMaterial(new GrassMicroMaterial, materialKey(grass))
MicroMaterialRegistry.remapName(oldKey(mycelium), materialKey(mycelium))
registerMaterial(new TopMicroMaterial(mycelium), materialKey(mycelium))
}
createAndRegister(stone)
createAndRegister(dirt, 0 to 2)
createAndRegister(cobblestone)
createAndRegister(planks, 0 to 5)
createAndRegister(log, 0 to 3)
createAndRegister(log2, 0 to 1, "tile.log2")
createAndRegister(leaves, 0 to 3)
createAndRegister(leaves2, 0 to 1, "tile.leaves2")
createAndRegister(sponge)
createAndRegister(glass)
createAndRegister(lapis_block)
createAndRegister(sandstone, 0 to 2)
createAndRegister(wool, 0 to 15)
createAndRegister(gold_block)
createAndRegister(iron_block)
createAndRegister(brick_block)
createAndRegister(bookshelf)
createAndRegister(mossy_cobblestone)
createAndRegister(obsidian)
createAndRegister(diamond_block)
createAndRegister(ice)
createAndRegister(snow)
createAndRegister(clay)
createAndRegister(netherrack)
createAndRegister(soul_sand)
createAndRegister(glowstone)
createAndRegister(stonebrick, 0 to 3)
createAndRegister(nether_brick)
createAndRegister(end_stone)
createAndRegister(emerald_block)
createAndRegister(redstone_block)
createAndRegister(quartz_block)
createAndRegister(stained_hardened_clay, 0 to 15)
createAndRegister(hardened_clay)
createAndRegister(coal_block)
createAndRegister(packed_ice)
createAndRegister(stained_glass, 0 to 15)
MicroMaterialRegistry.remapName(oldKey(grass), materialKey(grass))
registerMaterial(new GrassMicroMaterial, materialKey(grass))
MicroMaterialRegistry.remapName(oldKey(mycelium), materialKey(mycelium))
registerMaterial(new TopMicroMaterial(mycelium), materialKey(mycelium))
}
}
@@ -20,217 +20,205 @@ import scala.collection.JavaConversions._
import codechicken.lib.data.MCDataInput
import codechicken.lib.lighting.LightMatrix
object EdgePlacement extends PlacementProperties
{
import codechicken.multipart.PartMap._
def microClass = EdgeMicroClass
def placementGrid = EdgePlacementGrid
def opposite(slot:Int, side:Int):Int =
{
if(slot < 0)//custom placement
return slot
val e = slot-15
return 15+ packEdgeBits(e, unpackEdgeBits(e)^(1<<(side>>1)))
}
override def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement =
{
if(pmt.size%2 == 1) return null
val part = PostMicroClass.create(pmt.world.isRemote, pmt.material)
part.setShape(pmt.size, pmt.hit.sideHit>>1)
if(pmt.doExpand)
{
val hpart = pmt.htile.partList(ExtendedMOP.getData[(Int, _)](pmt.hit)._1)
if(hpart.getType == PostMicroClass.getName)
{
val mpart = hpart.asInstanceOf[Microblock]
if(mpart.material == pmt.material && mpart.getSize + pmt.size < 8)
{
part.shape = ((mpart.getSize+pmt.size)<<4|mpart.getShape).toByte
return pmt.expand(mpart, part)
}
}
object EdgePlacement extends PlacementProperties {
import codechicken.multipart.PartMap._
def microClass = EdgeMicroClass
def placementGrid = EdgePlacementGrid
def opposite(slot: Int, side: Int): Int = {
if (slot < 0) // custom placement
return slot
val e = slot - 15
return 15 + packEdgeBits(e, unpackEdgeBits(e) ^ (1 << (side >> 1)))
}
override def customPlacement(
pmt: MicroblockPlacement
): ExecutablePlacement = {
if (pmt.size % 2 == 1) return null
val part = PostMicroClass.create(pmt.world.isRemote, pmt.material)
part.setShape(pmt.size, pmt.hit.sideHit >> 1)
if (pmt.doExpand) {
val hpart = pmt.htile.partList(ExtendedMOP.getData[(Int, _)](pmt.hit)._1)
if (hpart.getType == PostMicroClass.getName) {
val mpart = hpart.asInstanceOf[Microblock]
if (mpart.material == pmt.material && mpart.getSize + pmt.size < 8) {
part.shape = ((mpart.getSize + pmt.size) << 4 | mpart.getShape).toByte
return pmt.expand(mpart, part)
}
if(pmt.slot >= 0)
return null
if(pmt.internal && !pmt.oppMod)
return pmt.internalPlacement(pmt.htile.asInstanceOf[TileMultipart], part)
return pmt.externalPlacement(part)
}
}
if (pmt.slot >= 0)
return null
if (pmt.internal && !pmt.oppMod)
return pmt.internalPlacement(pmt.htile.asInstanceOf[TileMultipart], part)
return pmt.externalPlacement(part)
}
}
object EdgeMicroClass extends CommonMicroClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 12)
{
val rx = if((s&2) != 0) -1 else 1
val rz = if((s&1) != 0) -1 else 1
val transform = new TransformationList(new Scale(new Vector3(rx, 1, rz)), AxisCycle.cycles(s>>2)).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, 1, d).apply(transform)
}
object EdgeMicroClass extends CommonMicroClass {
var aBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 12) {
val rx = if ((s & 2) != 0) -1 else 1
val rz = if ((s & 1) != 0) -1 else 1
val transform = new TransformationList(
new Scale(new Vector3(rx, 1, rz)),
AxisCycle.cycles(s >> 2)
).at(center)
for (t <- 1 until 8) {
val d = t / 8d
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, d, 1, d).apply(transform)
}
override def itemSlot = 15
def getName = "mcr_edge"
}
def baseTrait = classOf[EdgeMicroblock]
def clientTrait = classOf[CommonMicroblockClient]
override def itemSlot = 15
def placementProperties = EdgePlacement
def getName = "mcr_edge"
def getResistanceFactor = 0.5F
def baseTrait = classOf[EdgeMicroblock]
def clientTrait = classOf[CommonMicroblockClient]
def placementProperties = EdgePlacement
def getResistanceFactor = 0.5f
}
trait EdgeMicroblock extends CommonMicroblock with TEdgePart
{
override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-15)).toByte
trait EdgeMicroblock extends CommonMicroblock with TEdgePart {
override def setShape(size: Int, slot: Int) = shape =
(size << 4 | (slot - 15)).toByte
def microClass = EdgeMicroClass
def getBounds = EdgeMicroClass.aBounds(shape)
override def getSlot = getShape+15
def microClass = EdgeMicroClass
def getBounds = EdgeMicroClass.aBounds(shape)
override def getSlot = getShape + 15
}
object PostMicroClass extends MicroblockClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 3)
{
val transform = sideRotations(s<<1).at(center)
for(t <- 2 until 8 by 2)
{
val d1 = 0.5-t/16D
val d2 = 0.5+t/16D
aBounds(t<<4|s) = new Cuboid6(d1, 0, d1, d2, 1, d2).apply(transform)
}
object PostMicroClass extends MicroblockClass {
var aBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 3) {
val transform = sideRotations(s << 1).at(center)
for (t <- 2 until 8 by 2) {
val d1 = 0.5 - t / 16d
val d2 = 0.5 + t / 16d
aBounds(t << 4 | s) = new Cuboid6(d1, 0, d1, d2, 1, d2).apply(transform)
}
def getName = "mcr_post"
}
def baseTrait = classOf[PostMicroblock]
def clientTrait = classOf[PostMicroblockClient]
def getName = "mcr_post"
def getResistanceFactor = 0.5F
def baseTrait = classOf[PostMicroblock]
def clientTrait = classOf[PostMicroblockClient]
def getResistanceFactor = 0.5f
}
trait PostMicroblockClient extends PostMicroblock with MicroblockClient
{
var renderBounds1:Cuboid6 = _
var renderBounds2:Cuboid6 = _
override def render(pos:Vector3, pass:Int)
{
val mat = getIMaterial
if(pass == -1)
MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0)
else {
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0)
if (renderBounds2 != null)
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0)
}
}
override def onPartChanged(part:TMultiPart)
{
recalcBounds()
}
override def onAdded()
{
super.onAdded()
recalcBounds()
}
trait PostMicroblockClient extends PostMicroblock with MicroblockClient {
var renderBounds1: Cuboid6 = _
var renderBounds2: Cuboid6 = _
override def read(packet:MCDataInput)
{
super.read(packet)
recalcBounds()
override def render(pos: Vector3, pass: Int) {
val mat = getIMaterial
if (pass == -1)
MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0)
else {
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0)
if (renderBounds2 != null)
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0)
}
def recalcBounds()
{
renderBounds1 = getBounds.copy
renderBounds2 = null
shrinkFace(getShape<<1)
shrinkFace(getShape<<1|1)
tile.partList.foreach(p =>
if(p.isInstanceOf[PostMicroblock] && p != this)
shrinkPost(p.asInstanceOf[PostMicroblock])
)
}
def shrinkFace(fside:Int)
{
val part = tile.partMap(fside)
if(part != null && part.getType.equals("mcr_face"))
MicroOcclusion.shrink(renderBounds1, part.asInstanceOf[CommonMicroblock].getBounds, fside)
}
def shrinkPost(post:PostMicroblock)
{
if(post == this)
return
if(thisShrinks(post))
{
if(renderBounds2 == null)
renderBounds2 = getBounds.copy
MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape<<1|1)
MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape<<1)
}
}
def thisShrinks(other:PostMicroblock):Boolean =
{
if(getSize != other.getSize) return getSize < other.getSize
if(isTransparent != other.isTransparent) return isTransparent
return getShape > other.getShape
}
override def onPartChanged(part: TMultiPart) {
recalcBounds()
}
override def onAdded() {
super.onAdded()
recalcBounds()
}
override def read(packet: MCDataInput) {
super.read(packet)
recalcBounds()
}
def recalcBounds() {
renderBounds1 = getBounds.copy
renderBounds2 = null
shrinkFace(getShape << 1)
shrinkFace(getShape << 1 | 1)
tile.partList.foreach(p =>
if (p.isInstanceOf[PostMicroblock] && p != this)
shrinkPost(p.asInstanceOf[PostMicroblock])
)
}
def shrinkFace(fside: Int) {
val part = tile.partMap(fside)
if (part != null && part.getType.equals("mcr_face"))
MicroOcclusion.shrink(
renderBounds1,
part.asInstanceOf[CommonMicroblock].getBounds,
fside
)
}
def shrinkPost(post: PostMicroblock) {
if (post == this)
return
if (thisShrinks(post)) {
if (renderBounds2 == null)
renderBounds2 = getBounds.copy
MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape << 1 | 1)
MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape << 1)
}
}
def thisShrinks(other: PostMicroblock): Boolean = {
if (getSize != other.getSize) return getSize < other.getSize
if (isTransparent != other.isTransparent) return isTransparent
return getShape > other.getShape
}
}
trait PostMicroblock extends Microblock with JPartialOcclusion with TNormalOcclusion
{
def microClass = PostMicroClass
trait PostMicroblock
extends Microblock
with JPartialOcclusion
with TNormalOcclusion {
def microClass = PostMicroClass
def getBounds = PostMicroClass.aBounds(shape)
def getOcclusionBoxes = Seq(getBounds)
def getPartialOcclusionBoxes = getOcclusionBoxes
override def itemClassID = EdgeMicroClass.getClassId
override def occlusionTest(npart:TMultiPart): Boolean =
{
if(npart.isInstanceOf[PostMicroblock])
return npart.asInstanceOf[PostMicroblock].getShape != getShape
if(npart.getType.equals("mcr_face"))
if(npart.asInstanceOf[CommonMicroblock].getSlot>>1 == getShape)
return true
def getBounds = PostMicroClass.aBounds(shape)
return super.occlusionTest(npart)
}
def getOcclusionBoxes = Seq(getBounds)
def getResistanceFactor = PostMicroClass.getResistanceFactor
def getPartialOcclusionBoxes = getOcclusionBoxes
override def canPlaceTorchOnTop = getShape == 0
}
override def itemClassID = EdgeMicroClass.getClassId
override def occlusionTest(npart: TMultiPart): Boolean = {
if (npart.isInstanceOf[PostMicroblock])
return npart.asInstanceOf[PostMicroblock].getShape != getShape
if (npart.getType.equals("mcr_face"))
if (npart.asInstanceOf[CommonMicroblock].getSlot >> 1 == getShape)
return true
return super.occlusionTest(npart)
}
def getResistanceFactor = PostMicroClass.getResistanceFactor
override def canPlaceTorchOnTop = getShape == 0
}
@@ -7,67 +7,79 @@ import codechicken.lib.vec.Rotation
import Rotation._
import Vector3._
object FacePlacement extends PlacementProperties
{
def microClass = FaceMicroClass
def placementGrid = FacePlacementGrid
def opposite(slot:Int, side:Int) = slot^1
override def expand(slot:Int, side:Int) = sneakOpposite(slot, side)
override def sneakOpposite(slot:Int, side:Int) = slot == (side^1)
object FacePlacement extends PlacementProperties {
def microClass = FaceMicroClass
def placementGrid = FacePlacementGrid
def opposite(slot: Int, side: Int) = slot ^ 1
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
}
class FaceMicroClass
object FaceMicroClass extends CommonMicroClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 6)
{
val transform = sideRotations(s).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, 1, d, 1).apply(transform)
}
object FaceMicroClass extends CommonMicroClass {
var aBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 6) {
val transform = sideRotations(s).at(center)
for (t <- 1 until 8) {
val d = t / 8d
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, 1, d, 1).apply(transform)
}
def getName = "mcr_face"
}
def itemSlot = 3
def getName = "mcr_face"
def baseTrait = classOf[FaceMicroblock]
def clientTrait = classOf[FaceMicroblockClient]
def placementProperties = FacePlacement
def itemSlot = 3
def getResistanceFactor = 1
def baseTrait = classOf[FaceMicroblock]
def clientTrait = classOf[FaceMicroblockClient]
def placementProperties = FacePlacement
def getResistanceFactor = 1
}
trait FaceMicroblockClient extends CommonMicroblockClient
{
override def render(pos:Vector3, pass:Int) {
if(pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else if(isTransparent)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
else {
val mat = getIMaterial
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds, renderMask | 1<<getSlot)
MicroblockRender.renderCuboid(pos, mat, pass, Cuboid6.full, ~(1<<getSlot))
}
trait FaceMicroblockClient extends CommonMicroblockClient {
override def render(pos: Vector3, pass: Int) {
if (pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else if (isTransparent)
MicroblockRender.renderCuboid(
pos,
getIMaterial,
pass,
renderBounds,
renderMask
)
else {
val mat = getIMaterial
MicroblockRender.renderCuboid(
pos,
mat,
pass,
renderBounds,
renderMask | 1 << getSlot
)
MicroblockRender.renderCuboid(
pos,
mat,
pass,
Cuboid6.full,
~(1 << getSlot)
)
}
}
}
trait FaceMicroblock extends CommonMicroblock with TFacePart
{
def microClass = FaceMicroClass
def getBounds = FaceMicroClass.aBounds(shape)
override def solid(side:Int) = getIMaterial.isSolid
trait FaceMicroblock extends CommonMicroblock with TFacePart {
def microClass = FaceMicroClass
def getBounds = FaceMicroClass.aBounds(shape)
override def solid(side: Int) = getIMaterial.isSolid
}
@@ -8,41 +8,52 @@ import net.minecraft.init.Blocks
import codechicken.lib.render.uv.{UVTranslation, IconTransformation}
import codechicken.lib.render.BlockRenderer.BlockFace
class GrassMicroMaterial extends BlockMicroMaterial(Blocks.grass, 0)
{
var sideIconT:IconTransformation = _
override def loadIcons()
{
super.loadIcons()
sideIconT = new IconTransformation(BlockGrass.getIconSideOverlay)
}
class GrassMicroMaterial extends BlockMicroMaterial(Blocks.grass, 0) {
var sideIconT: IconTransformation = _
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
val face = CCRenderState.model.asInstanceOf[BlockFace]
if(pass != -1)
face.computeLightCoords()
override def loadIcons() {
super.loadIcons()
sideIconT = new IconTransformation(BlockGrass.getIconSideOverlay)
}
if(face.side == 1)
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
else
MaterialRenderHelper.start(pos, pass, icont).lighting().render()
override def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
val face = CCRenderState.model.asInstanceOf[BlockFace]
if (pass != -1)
face.computeLightCoords()
if(face.side > 1)
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ sideIconT)
.blockColour(getColour(pass)).lighting().render()
}
if (face.side == 1)
MaterialRenderHelper
.start(pos, pass, icont)
.blockColour(getColour(pass))
.lighting()
.render()
else
MaterialRenderHelper.start(pos, pass, icont).lighting().render()
if (face.side > 1)
MaterialRenderHelper
.start(pos, pass, new UVTranslation(0, bounds.max.y - 1) ++ sideIconT)
.blockColour(getColour(pass))
.lighting()
.render()
}
}
class TopMicroMaterial($block:Block, $meta:Int = 0) extends BlockMicroMaterial($block, $meta)
{
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6)
{
val face = CCRenderState.model.asInstanceOf[BlockFace]
if(face.side <= 1)
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
else
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ icont)
.blockColour(getColour(pass)).lighting().render()
}
}
class TopMicroMaterial($block: Block, $meta: Int = 0)
extends BlockMicroMaterial($block, $meta) {
override def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
val face = CCRenderState.model.asInstanceOf[BlockFace]
if (face.side <= 1)
MaterialRenderHelper
.start(pos, pass, icont)
.blockColour(getColour(pass))
.lighting()
.render()
else
MaterialRenderHelper
.start(pos, pass, new UVTranslation(0, bounds.max.y - 1) ++ icont)
.blockColour(getColour(pass))
.lighting()
.render()
}
}
@@ -16,226 +16,401 @@ import org.lwjgl.opengl.GL11
import codechicken.lib.render.uv.IconTransformation
import codechicken.lib.lighting.LightMatrix
object HollowPlacement extends PlacementProperties
{
object HollowPlacementGrid extends FaceEdgeGrid(3 / 8D)
object HollowPlacement extends PlacementProperties {
object HollowPlacementGrid extends FaceEdgeGrid(3 / 8d)
def microClass = HollowMicroClass
def microClass = HollowMicroClass
def placementGrid = HollowPlacementGrid
def placementGrid = HollowPlacementGrid
def opposite(slot: Int, side: Int) = slot ^ 1
def opposite(slot: Int, side: Int) = slot ^ 1
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
}
object HollowMicroClass extends CommonMicroClass
{
var pBoxes: Array[Seq[Cuboid6]] = new Array(256)
var occBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 6) {
val transform = sideRotations(s).at(center)
for (t <- 1 until 8) {
val d = t / 8D
val w1 = 1 / 8D
val w2 = 3 / 16D
pBoxes(t << 4 | s) = Seq(
new Cuboid6(0, 0, 0, w1, d, 1),
new Cuboid6(1 - w1, 0, 0, 1, d, 1),
new Cuboid6(w1, 0, 0, 1 - w1, d, w1),
new Cuboid6(w1, 0, 1 - w1, 1 - w1, d, 1))
.map(_.apply(transform))
occBounds(t << 4 | s) = new Cuboid6(1 / 8D, 0, 1 / 8D, 7 / 8D, d, 7 / 8D).apply(transform)
}
object HollowMicroClass extends CommonMicroClass {
var pBoxes: Array[Seq[Cuboid6]] = new Array(256)
var occBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 6) {
val transform = sideRotations(s).at(center)
for (t <- 1 until 8) {
val d = t / 8d
val w1 = 1 / 8d
val w2 = 3 / 16d
pBoxes(t << 4 | s) = Seq(
new Cuboid6(0, 0, 0, w1, d, 1),
new Cuboid6(1 - w1, 0, 0, 1, d, 1),
new Cuboid6(w1, 0, 0, 1 - w1, d, w1),
new Cuboid6(w1, 0, 1 - w1, 1 - w1, d, 1)
)
.map(_.apply(transform))
occBounds(t << 4 | s) =
new Cuboid6(1 / 8d, 0, 1 / 8d, 7 / 8d, d, 7 / 8d).apply(transform)
}
}
def getName = "mcr_hllw"
def getName = "mcr_hllw"
def baseTrait = classOf[HollowMicroblock]
def clientTrait = classOf[HollowMicroblockClient]
def baseTrait = classOf[HollowMicroblock]
def clientTrait = classOf[HollowMicroblockClient]
def itemSlot = 3
def itemSlot = 3
def placementProperties = HollowPlacement
def placementProperties = HollowPlacement
def getResistanceFactor = 1
def getResistanceFactor = 1
}
trait HollowMicroblockClient extends HollowMicroblock with CommonMicroblockClient
{
renderMask |= 8 << 8
trait HollowMicroblockClient
extends HollowMicroblock
with CommonMicroblockClient {
renderMask |= 8 << 8
override def recalcBounds() {
super.recalcBounds()
renderMask = renderMask & 0xFF | getHollowSize << 8
override def recalcBounds() {
super.recalcBounds()
renderMask = renderMask & 0xff | getHollowSize << 8
}
override def drawBreaking(renderBlocks: RenderBlocks) {
CCRenderState.reset()
CCRenderState.setPipeline(
new Translation(x, y, z),
new IconTransformation(renderBlocks.overrideBlockTexture)
)
renderHollow(
null,
0,
getBounds,
0,
false,
(
pos: Vector3,
mat: IMicroMaterial,
pass: Int,
c: Cuboid6,
sideMask: Int
) => BlockRenderer.renderCuboid(c, sideMask)
)
}
override def render(pos: Vector3, pass: Int) {
if (pass == -1)
renderHollow(
pos,
pass,
getBounds,
0,
false,
MicroblockRender.renderCuboid
)
else if (isTransparent)
renderHollow(
pos,
pass,
renderBounds,
renderMask,
false,
MicroblockRender.renderCuboid
)
else {
renderHollow(
pos,
pass,
renderBounds,
renderMask | 1 << getSlot,
false,
MicroblockRender.renderCuboid
)
renderHollow(
pos,
pass,
Cuboid6.full,
~(1 << getSlot),
true,
MicroblockRender.renderCuboid
)
}
}
override def drawBreaking(renderBlocks: RenderBlocks) {
CCRenderState.reset()
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
renderHollow(null, 0, getBounds, 0, false,
(pos: Vector3, mat: IMicroMaterial, pass: Int, c: Cuboid6, sideMask: Int) =>
BlockRenderer.renderCuboid(c, sideMask))
def renderHollow(
pos: Vector3,
pass: Int,
c: Cuboid6,
sideMask: Int,
face: Boolean,
f: (Vector3, IMicroMaterial, Int, Cuboid6, Int) => Unit
) {
val mat = getIMaterial
val size = renderMask >> 8
val d1 = 0.5 - size / 32d
val d2 = 0.5 + size / 32d
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
var iMask = 0
getSlot match {
case 0 | 1 =>
if (face)
iMask = 0x3c
f(
pos,
mat,
pass,
new Cuboid6(d1, y1, d2, d2, y2, z2),
0x3b | iMask
) // -z internal
f(
pos,
mat,
pass,
new Cuboid6(d1, y1, z1, d2, y2, d1),
0x37 | iMask
) // +z internal
f(
pos,
mat,
pass,
new Cuboid6(d2, y1, d1, x2, y2, d2),
sideMask & 0x23 | 0xc | iMask
) // -x internal -y+y+x external
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, d1, d1, y2, d2),
sideMask & 0x13 | 0xc | iMask
) // +x internal -y+y-x external
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, d2, x2, y2, z2),
sideMask & 0x3b | 4 | iMask
) // -y+y+z-x+x external
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, z1, x2, y2, d1),
sideMask & 0x37 | 8 | iMask
) // -y+y-z-x+x external
case 2 | 3 =>
if (face)
iMask = 0x33
f(
pos,
mat,
pass,
new Cuboid6(d2, d1, z1, x2, d2, z2),
0x2f | iMask
) // -x internal
f(
pos,
mat,
pass,
new Cuboid6(x1, d1, z1, d1, d2, z2),
0x1f | iMask
) // +x internal
f(
pos,
mat,
pass,
new Cuboid6(d1, d2, z1, d2, y2, z2),
sideMask & 0xe | 0x30 | iMask
) // -y internal -z+z+y external
f(
pos,
mat,
pass,
new Cuboid6(d1, y1, z1, d2, d1, z2),
sideMask & 0xd | 0x30 | iMask
) // +y internal -z+z-y external
f(
pos,
mat,
pass,
new Cuboid6(d2, y1, z1, x2, y2, z2),
sideMask & 0x2f | 0x10 | iMask
) // -z+z+x-y+y external
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, z1, d1, y2, z2),
sideMask & 0x1f | 0x20 | iMask
) // -z+z-x-y+y external
case 4 | 5 =>
if (face)
iMask = 0xf
f(
pos,
mat,
pass,
new Cuboid6(x1, d2, d1, x2, y2, d2),
0x3e | iMask
) // -y internal
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, d1, x2, d1, d2),
0x3d | iMask
) // +y internal
f(
pos,
mat,
pass,
new Cuboid6(x1, d1, d2, x2, d2, z2),
sideMask & 0x38 | 3 | iMask
) // -z internal -x+x+z external
f(
pos,
mat,
pass,
new Cuboid6(x1, d1, z1, x2, d2, d1),
sideMask & 0x34 | 3 | iMask
) // +z internal -x+x-z external
f(
pos,
mat,
pass,
new Cuboid6(x1, d2, z1, x2, y2, z2),
sideMask & 0x3e | 1 | iMask
) // -x+x+y-z+z external
f(
pos,
mat,
pass,
new Cuboid6(x1, y1, z1, x2, d1, z2),
sideMask & 0x3d | 2 | iMask
) // -x+x-y-z+z external
}
}
override def render(pos: Vector3, pass: Int) {
if (pass == -1)
renderHollow(pos, pass, getBounds, 0, false, MicroblockRender.renderCuboid)
else if (isTransparent)
renderHollow(pos, pass, renderBounds, renderMask, false, MicroblockRender.renderCuboid)
else {
renderHollow(pos, pass, renderBounds, renderMask | 1 << getSlot, false, MicroblockRender.renderCuboid)
renderHollow(pos, pass, Cuboid6.full, ~(1 << getSlot), true, MicroblockRender.renderCuboid)
}
}
override def drawHighlight(
hit: MovingObjectPosition,
player: EntityPlayer,
frame: Float
): Boolean = {
val size = getHollowSize
val d1 = 0.5 - size / 32d
val d2 = 0.5 + size / 32d
val t = (shape >> 4) / 8d
def renderHollow(pos: Vector3, pass: Int, c: Cuboid6, sideMask: Int, face: Boolean,
f: (Vector3, IMicroMaterial, Int, Cuboid6, Int) => Unit) {
val mat = getIMaterial
val size = renderMask >> 8
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
import GL11._
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDisable(GL_TEXTURE_2D)
glColor4f(0, 0, 0, 0.4f)
glLineWidth(2)
glDepthMask(false)
glPushMatrix()
RenderUtils.translateToWorldCoords(player, frame)
glTranslated(x, y, z)
sideRotations(shape & 0xf).at(center).glApply()
var iMask = 0
getSlot match {
case 0 | 1 =>
if (face)
iMask = 0x3C
f(pos, mat, pass, new Cuboid6(d1, y1, d2, d2, y2, z2), 0x3B | iMask) //-z internal
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, y2, d1), 0x37 | iMask) //+z internal
f(pos, mat, pass, new Cuboid6(d2, y1, d1, x2, y2, d2), sideMask & 0x23 | 0xC | iMask) //-x internal -y+y+x external
f(pos, mat, pass, new Cuboid6(x1, y1, d1, d1, y2, d2), sideMask & 0x13 | 0xC | iMask) //+x internal -y+y-x external
f(pos, mat, pass, new Cuboid6(x1, y1, d2, x2, y2, z2), sideMask & 0x3B | 4 | iMask) //-y+y+z-x+x external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, y2, d1), sideMask & 0x37 | 8 | iMask) //-y+y-z-x+x external
case 2 | 3 =>
if (face)
iMask = 0x33
f(pos, mat, pass, new Cuboid6(d2, d1, z1, x2, d2, z2), 0x2F | iMask) //-x internal
f(pos, mat, pass, new Cuboid6(x1, d1, z1, d1, d2, z2), 0x1F | iMask) //+x internal
f(pos, mat, pass, new Cuboid6(d1, d2, z1, d2, y2, z2), sideMask & 0xE | 0x30 | iMask) //-y internal -z+z+y external
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, d1, z2), sideMask & 0xD | 0x30 | iMask) //+y internal -z+z-y external
f(pos, mat, pass, new Cuboid6(d2, y1, z1, x2, y2, z2), sideMask & 0x2F | 0x10 | iMask) //-z+z+x-y+y external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, d1, y2, z2), sideMask & 0x1F | 0x20 | iMask) //-z+z-x-y+y external
case 4 | 5 =>
if (face)
iMask = 0xF
f(pos, mat, pass, new Cuboid6(x1, d2, d1, x2, y2, d2), 0x3E | iMask) //-y internal
f(pos, mat, pass, new Cuboid6(x1, y1, d1, x2, d1, d2), 0x3D | iMask) //+y internal
f(pos, mat, pass, new Cuboid6(x1, d1, d2, x2, d2, z2), sideMask & 0x38 | 3 | iMask) //-z internal -x+x+z external
f(pos, mat, pass, new Cuboid6(x1, d1, z1, x2, d2, d1), sideMask & 0x34 | 3 | iMask) //+z internal -x+x-z external
f(pos, mat, pass, new Cuboid6(x1, d2, z1, x2, y2, z2), sideMask & 0x3E | 1 | iMask) //-x+x+y-z+z external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, d1, z2), sideMask & 0x3D | 2 | iMask) //-x+x-y-z+z external
}
}
override def drawHighlight(hit: MovingObjectPosition, player: EntityPlayer, frame: Float): Boolean = {
val size = getHollowSize
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val t = (shape >> 4) / 8D
import GL11._
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDisable(GL_TEXTURE_2D)
glColor4f(0, 0, 0, 0.4F)
glLineWidth(2)
glDepthMask(false)
glPushMatrix()
RenderUtils.translateToWorldCoords(player, frame)
glTranslated(x, y, z)
sideRotations(shape & 0xF).at(center).glApply()
RenderUtils.drawCuboidOutline(new Cuboid6(0, 0, 0, 1, t, 1).expand(0.001))
RenderUtils.drawCuboidOutline(new Cuboid6(d1, 0, d1, d2, t, d2).expand(-0.001))
glPopMatrix()
glDepthMask(true)
glEnable(GL_TEXTURE_2D)
glDisable(GL_BLEND)
return true
}
RenderUtils.drawCuboidOutline(new Cuboid6(0, 0, 0, 1, t, 1).expand(0.001))
RenderUtils.drawCuboidOutline(
new Cuboid6(d1, 0, d1, d2, t, d2).expand(-0.001)
)
glPopMatrix()
glDepthMask(true)
glEnable(GL_TEXTURE_2D)
glDisable(GL_BLEND)
return true
}
}
trait HollowMicroblock extends CommonMicroblock with TFacePart with TNormalOcclusion
{
def microClass = HollowMicroClass
trait HollowMicroblock
extends CommonMicroblock
with TFacePart
with TNormalOcclusion {
def microClass = HollowMicroClass
def getBounds: Cuboid6 = FaceMicroClass.aBounds(shape)
def getBounds: Cuboid6 = FaceMicroClass.aBounds(shape)
override def getPartialOcclusionBoxes = HollowMicroClass.pBoxes(shape)
override def getPartialOcclusionBoxes = HollowMicroClass.pBoxes(shape)
def getHollowSize = tile match {
case null => 8
case _ => tile.partMap(6) match {
case part: ISidedHollowConnect => part.getHollowSize(getSlot)
case _ => 8
}
def getHollowSize = tile match {
case null => 8
case _ =>
tile.partMap(6) match {
case part: ISidedHollowConnect => part.getHollowSize(getSlot)
case _ => 8
}
}
def getOcclusionBoxes = {
val size = getHollowSize
val c = HollowMicroClass.occBounds(shape)
val d1 = 0.5 - size / 32d
val d2 = 0.5 + size / 32d
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
getSlot match {
case 0 | 1 =>
Seq(
new Cuboid6(d2, y1, d1, x2, y2, d2),
new Cuboid6(x1, y1, d1, d1, y2, d2),
new Cuboid6(x1, y1, d2, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, y2, d1)
)
case 2 | 3 =>
Seq(
new Cuboid6(d1, d2, z1, d2, y2, z2),
new Cuboid6(d1, y1, z1, d2, d1, z2),
new Cuboid6(d2, y1, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, d1, y2, z2)
)
case 4 | 5 =>
Seq(
new Cuboid6(x1, d1, d2, x2, d2, z2),
new Cuboid6(x1, d1, z1, x2, d2, d1),
new Cuboid6(x1, d2, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, d1, z2)
)
}
}
def getOcclusionBoxes = {
val size = getHollowSize
val c = HollowMicroClass.occBounds(shape)
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
override def getCollisionBoxes = {
val size = getHollowSize
val d1 = 0.5 - size / 32d
val d2 = 0.5 + size / 32d
val t = (shape >> 4) / 8d
getSlot match {
case 0 | 1 =>
Seq(new Cuboid6(d2, y1, d1, x2, y2, d2),
new Cuboid6(x1, y1, d1, d1, y2, d2),
new Cuboid6(x1, y1, d2, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, y2, d1))
case 2 | 3 =>
Seq(new Cuboid6(d1, d2, z1, d2, y2, z2),
new Cuboid6(d1, y1, z1, d2, d1, z2),
new Cuboid6(d2, y1, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, d1, y2, z2))
case 4 | 5 =>
Seq(new Cuboid6(x1, d1, d2, x2, d2, z2),
new Cuboid6(x1, d1, z1, x2, d2, d1),
new Cuboid6(x1, d2, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, d1, z2))
}
}
val tr = sideRotations(shape & 0xf).at(center)
Seq(
new Cuboid6(0, 0, 0, 1, t, d1),
new Cuboid6(0, 0, d2, 1, t, 1),
new Cuboid6(0, 0, d1, d1, t, d2),
new Cuboid6(d2, 0, d1, 1, t, d2)
)
.map(c => c.apply(tr))
}
override def getCollisionBoxes = {
val size = getHollowSize
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val t = (shape >> 4) / 8D
override def getSubParts =
getCollisionBoxes.map(c => new IndexedCuboid6(0, c))
val tr = sideRotations(shape & 0xF).at(center)
Seq(new Cuboid6(0, 0, 0, 1, t, d1),
new Cuboid6(0, 0, d2, 1, t, 1),
new Cuboid6(0, 0, d1, d1, t, d2),
new Cuboid6(d2, 0, d1, 1, t, d2))
.map(c => c.apply(tr))
}
override def allowCompleteOcclusion = true
override def getSubParts = getCollisionBoxes.map(c => new IndexedCuboid6(0, c))
override def solid(side: Int) = false
override def allowCompleteOcclusion = true
override def solid(side: Int) = false
override def redstoneConductionMap = 0x10
}
override def redstoneConductionMap = 0x10
}
@@ -3,20 +3,19 @@ package codechicken.microblock
import codechicken.lib.vec.Cuboid6
import net.minecraft.world.World
/**
* Wrapper class blocks/tiles/parts wanting to use the micro material system to render part of their models.
*/
/** Wrapper class blocks/tiles/parts wanting to use the micro material system to
* render part of their models.
*/
trait IMicroMaterialRender {
/**
* May be null for inventory rendering.
*/
def world:World
def x:Int
def y:Int
def z:Int
/**
* Return the bounds of the part for texture mapping side decals like grass
*/
def getRenderBounds:Cuboid6
}
/** May be null for inventory rendering.
*/
def world: World
def x: Int
def y: Int
def z: Int
/** Return the bounds of the part for texture mapping side decals like grass
*/
def getRenderBounds: Cuboid6
}
@@ -1,13 +1,15 @@
package codechicken.microblock
/**
* Implement this on center parts that connect through hollow covers to have the covers render their hollows to fit the connection size
*/
trait ISidedHollowConnect
{
/**
* @param side The side of the block on which the cover resides
* @return The size (width and height) of the connection in pixels. Must be be less than 12 and more than 0
*/
def getHollowSize(side:Int):Int
}
/** Implement this on center parts that connect through hollow covers to have
* the covers render their hollows to fit the connection size
*/
trait ISidedHollowConnect {
/** @param side
* The side of the block on which the cover resides
* @return
* The size (width and height) of the connection in pixels. Must be be less
* than 12 and more than 0
*/
def getHollowSize(side: Int): Int
}
@@ -23,148 +23,184 @@ import codechicken.lib.render.TextureUtils
import net.minecraft.util.MovingObjectPosition.MovingObjectType
import CommonMicroClass._
class ItemMicroPart extends Item
{
setUnlocalizedName("microblock")
setHasSubtypes(true)
override def getItemStackDisplayName(stack:ItemStack):String =
{
val material = getMaterial(stack)
val mcrClass = getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage&0xFF
if(material == null || mcrClass == null)
return "Unnamed"
return StatCollector.translateToLocalFormatted(mcrClass.getName+"."+size+".name", material.getLocalizedName)
class ItemMicroPart extends Item {
setUnlocalizedName("microblock")
setHasSubtypes(true)
override def getItemStackDisplayName(stack: ItemStack): String = {
val material = getMaterial(stack)
val mcrClass = getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage & 0xff
if (material == null || mcrClass == null)
return "Unnamed"
return StatCollector.translateToLocalFormatted(
mcrClass.getName + "." + size + ".name",
material.getLocalizedName
)
}
override def getSubItems(item: Item, tab: CreativeTabs, list$ : List[_]) {
val list = list$.asInstanceOf[List[ItemStack]]
for (classId <- 0 until classes.length) {
val mcrClass = classes(classId)
if (mcrClass != null)
for (size <- Seq(1, 2, 4))
MicroMaterialRegistry.getIdMap.foreach(e =>
list.add(create(classId << 8 | size, e._1))
)
}
override def getSubItems(item:Item, tab:CreativeTabs, list$:List[_])
{
val list = list$.asInstanceOf[List[ItemStack]]
for(classId <- 0 until classes.length)
{
val mcrClass = classes(classId)
if(mcrClass != null)
for(size <- Seq(1, 2, 4))
MicroMaterialRegistry.getIdMap.foreach(e => list.add(create(classId<<8|size, e._1)))
}
}
override def registerIcons(register:IIconRegister){}
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 = getMicroClass(item.getItemDamage)
val size = item.getItemDamage&0xFF
if(material < 0 || mcrClass == null)
return false
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit != null && hit.typeOfHit == MovingObjectType.BLOCK)
{
val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties)
if(placement == null)
return false
if(!world.isRemote)
{
placement.place(world, player, item)
if(!player.capabilities.isCreativeMode)
placement.consume(world, player, item)
val sound = MicroMaterialRegistry.getMaterial(material).getSound
if(sound != null)
world.playSoundEffect(placement.pos.x + 0.5D, placement.pos.y + 0.5D, placement.pos.z + 0.5D, sound.func_150496_b, (sound.getVolume + 1.0F) / 2.0F, sound.getPitch * 0.8F)
}
return true
}
}
override def registerIcons(register: IIconRegister) {}
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 = getMicroClass(item.getItemDamage)
val size = item.getItemDamage & 0xff
if (material < 0 || mcrClass == null)
return false
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if (hit != null && hit.typeOfHit == MovingObjectType.BLOCK) {
val placement = MicroblockPlacement(
player,
hit,
size,
material,
!player.capabilities.isCreativeMode,
mcrClass.placementProperties
)
if (placement == null)
return false
if (!world.isRemote) {
placement.place(world, player, item)
if (!player.capabilities.isCreativeMode)
placement.consume(world, player, item)
val sound = MicroMaterialRegistry.getMaterial(material).getSound
if (sound != null)
world.playSoundEffect(
placement.pos.x + 0.5d,
placement.pos.y + 0.5d,
placement.pos.z + 0.5d,
sound.func_150496_b,
(sound.getVolume + 1.0f) / 2.0f,
sound.getPitch * 0.8f
)
}
return true
}
return false
}
}
object ItemMicroPart
{
def checkTagCompound(stack:ItemStack)
{
if(!stack.hasTagCompound)
stack.setTagCompound(new NBTTagCompound())
}
def create(damage:Int, material:Int):ItemStack = create(damage, MicroMaterialRegistry.materialName(material))
def create(damage:Int, material:String):ItemStack = create(1, damage, material)
def create(amount:Int, damage:Int, material:String):ItemStack =
{
val stack = new ItemStack(MicroblockProxy.itemMicro, amount, damage)
checkTagCompound(stack)
stack.getTagCompound.setString("mat", material)
return stack
}
def getMaterial(stack:ItemStack):IMicroMaterial =
{
checkTagCompound(stack)
if(!stack.getTagCompound.hasKey("mat"))
return null
object ItemMicroPart {
def checkTagCompound(stack: ItemStack) {
if (!stack.hasTagCompound)
stack.setTagCompound(new NBTTagCompound())
}
return MicroMaterialRegistry.getMaterial(stack.getTagCompound.getString("mat"))
}
def getMaterialID(stack:ItemStack):Int =
{
checkTagCompound(stack)
if(!stack.getTagCompound.hasKey("mat"))
return 0
def create(damage: Int, material: Int): ItemStack =
create(damage, MicroMaterialRegistry.materialName(material))
return MicroMaterialRegistry.materialID(stack.getTagCompound.getString("mat"))
}
def create(damage: Int, material: String): ItemStack =
create(1, damage, material)
def create(amount: Int, damage: Int, material: String): ItemStack = {
val stack = new ItemStack(MicroblockProxy.itemMicro, amount, damage)
checkTagCompound(stack)
stack.getTagCompound.setString("mat", material)
return stack
}
def getMaterial(stack: ItemStack): IMicroMaterial = {
checkTagCompound(stack)
if (!stack.getTagCompound.hasKey("mat"))
return null
return MicroMaterialRegistry.getMaterial(
stack.getTagCompound.getString("mat")
)
}
def getMaterialID(stack: ItemStack): Int = {
checkTagCompound(stack)
if (!stack.getTagCompound.hasKey("mat"))
return 0
return MicroMaterialRegistry.materialID(
stack.getTagCompound.getString("mat")
)
}
}
object ItemMicroPartRenderer extends IItemRenderer
{
def handleRenderType(item:ItemStack, t:ItemRenderType) = true
def shouldUseRenderHelper(t:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
def renderItem(t:ItemRenderType, item:ItemStack, data:Object*)
{
val material = getMaterial(item)
val mcrClass = getMicroClass(item.getItemDamage)
val size = item.getItemDamage&0xFF
if(material == null || mcrClass == null)
return
GL11.glPushMatrix()
if(t == ItemRenderType.ENTITY)
GL11.glScaled(0.5, 0.5, 0.5)
if(t == ItemRenderType.INVENTORY || t == ItemRenderType.ENTITY)
GL11.glTranslatef(-0.5F, -0.5F, -0.5F)
object ItemMicroPartRenderer extends IItemRenderer {
def handleRenderType(item: ItemStack, t: ItemRenderType) = true
TextureUtils.bindAtlas(0)
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.startDrawing()
val part = mcrClass.create(true, getMaterialID(item)).asInstanceOf[MicroblockClient]
part.setShape(size, mcrClass.itemSlot)
part.render(new Vector3(0.5, 0.5, 0.5).subtract(part.getBounds.center), -1)
CCRenderState.draw()
GL11.glPopMatrix()
}
def renderHighlight(player:EntityPlayer, stack:ItemStack, hit:MovingObjectPosition):Boolean =
{
val material = getMaterialID(stack)
val mcrClass = getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage&0xFF
if(material < 0 || mcrClass == null)
return false
return MicroMaterialRegistry.renderHighlight(player, hit, mcrClass, size, material)
}
}
def shouldUseRenderHelper(
t: ItemRenderType,
item: ItemStack,
helper: ItemRendererHelper
) = true
def renderItem(t: ItemRenderType, item: ItemStack, data: Object*) {
val material = getMaterial(item)
val mcrClass = getMicroClass(item.getItemDamage)
val size = item.getItemDamage & 0xff
if (material == null || mcrClass == null)
return
GL11.glPushMatrix()
if (t == ItemRenderType.ENTITY)
GL11.glScaled(0.5, 0.5, 0.5)
if (t == ItemRenderType.INVENTORY || t == ItemRenderType.ENTITY)
GL11.glTranslatef(-0.5f, -0.5f, -0.5f)
TextureUtils.bindAtlas(0)
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.startDrawing()
val part =
mcrClass.create(true, getMaterialID(item)).asInstanceOf[MicroblockClient]
part.setShape(size, mcrClass.itemSlot)
part.render(new Vector3(0.5, 0.5, 0.5).subtract(part.getBounds.center), -1)
CCRenderState.draw()
GL11.glPopMatrix()
}
def renderHighlight(
player: EntityPlayer,
stack: ItemStack,
hit: MovingObjectPosition
): Boolean = {
val material = getMaterialID(stack)
val mcrClass = getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage & 0xff
if (material < 0 || mcrClass == null)
return false
return MicroMaterialRegistry.renderHighlight(
player,
hit,
mcrClass,
size,
material
)
}
}
@@ -21,79 +21,116 @@ import net.minecraft.client.renderer.texture.{TextureMap, IIconRegister}
import codechicken.lib.render.uv.UVTranslation
import codechicken.microblock.handler.MicroblockProxy
/**
* Interface for items that are 'saws'
*/
trait Saw extends Item
{
/**
* The maximum harvest level that some version of this saw is capable of cutting
*/
def getMaxCuttingStrength:Int = getCuttingStrength(new ItemStack(this))
/**
* The harvest level this saw is capable of cutting
*/
def getCuttingStrength(item:ItemStack):Int
/** Interface for items that are 'saws'
*/
trait Saw extends Item {
/** The maximum harvest level that some version of this saw is capable of
* cutting
*/
def getMaxCuttingStrength: Int = getCuttingStrength(new ItemStack(this))
/** The harvest level this saw is capable of cutting
*/
def getCuttingStrength(item: ItemStack): Int
}
class ItemSaw(sawTag:ConfigTag, val harvestLevel:Int) extends Item with Saw
{
{
val maxDamage = sawTag.getTag("durability").getIntValue(1<<harvestLevel+8)
if(maxDamage > 0)
setMaxDamage(maxDamage)
setNoRepair()
setMaxStackSize(1)
setCreativeTab(net.minecraft.creativetab.CreativeTabs.tabTools)
class ItemSaw(sawTag: ConfigTag, val harvestLevel: Int) extends Item with Saw {
{
val maxDamage =
sawTag.getTag("durability").getIntValue(1 << harvestLevel + 8)
if (maxDamage > 0)
setMaxDamage(maxDamage)
setNoRepair()
setMaxStackSize(1)
setCreativeTab(net.minecraft.creativetab.CreativeTabs.tabTools)
}
override def hasContainerItem = true
override def getContainerItem(stack: ItemStack) =
if (isDamageable)
new ItemStack(stack.getItem, 1, stack.getItemDamage + 1)
else
stack
override def doesContainerItemLeaveCraftingGrid(stack: ItemStack) = false
def getCuttingStrength(item: ItemStack) = harvestLevel
}
object ItemSawRenderer extends IItemRenderer {
val models = CCModel.parseObjModels(
new ResourceLocation("microblock", "models/saw.obj"),
7,
new SwapYZ()
)
val handle = models.get("Handle")
val holder = models.get("BladeSupport")
val blade = models.get("Blade")
def handleRenderType(item: ItemStack, renderType: ItemRenderType) =
!MicroblockProxy.useSawIcons || TextureUtils.isMissing(
item.getIconIndex,
TextureMap.locationItemsTexture
)
def shouldUseRenderHelper(
renderType: ItemRenderType,
item: ItemStack,
helper: ItemRendererHelper
) = true
def renderItem(renderType: ItemRenderType, item: ItemStack, data: Object*) {
val t = renderType match {
case INVENTORY =>
new TransformationList(
new Scale(1.8),
new Translation(0, 0, -0.6),
new Rotation(-pi / 4, 1, 0, 0),
new Rotation(pi * 3 / 4, 0, 1, 0)
)
case ENTITY =>
new TransformationList(
new Scale(1),
new Translation(0, 0, -0.25),
new Rotation(-pi / 4, 1, 0, 0)
)
case EQUIPPED_FIRST_PERSON =>
new TransformationList(
new Scale(1.5),
new Rotation(-pi / 3, 1, 0, 0),
new Rotation(pi * 3 / 4, 0, 1, 0),
new Translation(0.5, 0.5, 0.5)
)
case EQUIPPED =>
new TransformationList(
new Scale(1.5),
new Rotation(-pi / 5, 1, 0, 0),
new Rotation(-pi * 3 / 4, 0, 1, 0),
new Translation(0.75, 0.5, 0.75)
)
case _ => return
}
override def hasContainerItem = true
override def getContainerItem(stack:ItemStack) =
if(isDamageable)
new ItemStack(stack.getItem, 1, stack.getItemDamage+1)
else
stack
override def doesContainerItemLeaveCraftingGrid(stack:ItemStack) = false
def getCuttingStrength(item:ItemStack) = harvestLevel
}
object ItemSawRenderer extends IItemRenderer
{
val models = CCModel.parseObjModels(new ResourceLocation("microblock", "models/saw.obj"), 7, new SwapYZ())
val handle = models.get("Handle")
val holder = models.get("BladeSupport")
val blade = models.get("Blade")
def handleRenderType(item:ItemStack, renderType:ItemRenderType) =
!MicroblockProxy.useSawIcons || TextureUtils.isMissing(item.getIconIndex, TextureMap.locationItemsTexture)
def shouldUseRenderHelper(renderType:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
def renderItem(renderType:ItemRenderType, item:ItemStack, data:Object*)
{
val t = renderType match {
case INVENTORY => new TransformationList(new Scale(1.8), new Translation(0, 0, -0.6), new Rotation(-pi/4, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0))
case ENTITY => new TransformationList(new Scale(1), new Translation(0, 0, -0.25), new Rotation(-pi/4, 1, 0, 0))
case EQUIPPED_FIRST_PERSON => new TransformationList(new Scale(1.5), new Rotation(-pi/3, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0), new Translation(0.5, 0.5, 0.5))
case EQUIPPED => new TransformationList(new Scale(1.5), new Rotation(-pi/5, 1, 0, 0), new Rotation(-pi*3/4, 0, 1, 0), new Translation(0.75, 0.5, 0.75))
case _ => return
}
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.changeTexture("microblock:textures/items/saw.png")
CCRenderState.startDrawing()
handle.render(t)
holder.render(t)
CCRenderState.draw()
GL11.glDisable(GL11.GL_CULL_FACE)
CCRenderState.startDrawing()
blade.render(t, new UVTranslation(0, (item.getItem.asInstanceOf[Saw].getCuttingStrength(item)-1)*4/64D))
CCRenderState.draw()
GL11.glEnable(GL11.GL_CULL_FACE)
}
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.changeTexture("microblock:textures/items/saw.png")
CCRenderState.startDrawing()
handle.render(t)
holder.render(t)
CCRenderState.draw()
GL11.glDisable(GL11.GL_CULL_FACE)
CCRenderState.startDrawing()
blade.render(
t,
new UVTranslation(
0,
(item.getItem.asInstanceOf[Saw].getCuttingStrength(item) - 1) * 4 / 64d
)
)
CCRenderState.draw()
GL11.glEnable(GL11.GL_CULL_FACE)
}
}
@@ -19,220 +19,227 @@ import scala.collection.JavaConversions._
import java.util.{Iterator => JIterator}
import net.minecraft.block.Block.SoundType
object MicroMaterialRegistry
{
/**
* Interface for defining a micro material
*/
trait IMicroMaterial
{
/**
* The icon to be used for breaking particles on side
*/
@SideOnly(Side.CLIENT)
def getBreakingIcon(side: Int): IIcon
object MicroMaterialRegistry {
/**
* Callback to load icons from the underlying block/etc
*/
@SideOnly(Side.CLIENT)
def loadIcons() {}
/** Interface for defining a micro material
*/
trait IMicroMaterial {
/**
* This function must configure and use the CCRenderState pipeline to draw from the current vertex source.
* CCRenderState.lightMatrix will be set.
* @param pass The current render pass, -1 for inventory rendering
* @param bounds The cuboid bounds of the face being rendered
*/
@SideOnly(Side.CLIENT)
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6)
/** The icon to be used for breaking particles on side
*/
@SideOnly(Side.CLIENT)
def getBreakingIcon(side: Int): IIcon
/**
* Get the render pass for which this material renders in.
*/
@SideOnly(Side.CLIENT)
def canRenderInPass(pass: Int) = pass == 0
/** Callback to load icons from the underlying block/etc
*/
@SideOnly(Side.CLIENT)
def loadIcons() {}
/**
* Return true if this material is not opaque (glass, ice).
*/
def isTransparent: Boolean
/** This function must configure and use the CCRenderState pipeline to draw
* from the current vertex source. CCRenderState.lightMatrix will be set.
* @param pass
* The current render pass, -1 for inventory rendering
* @param bounds
* The cuboid bounds of the face being rendered
*/
@SideOnly(Side.CLIENT)
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6)
/**
* Return the light level emitted by this material (glowstone)
*/
def getLightValue: Int
/** Get the render pass for which this material renders in.
*/
@SideOnly(Side.CLIENT)
def canRenderInPass(pass: Int) = pass == 0
/**
* Return the strength of this material
*/
def getStrength(player: EntityPlayer): Float
/** Return true if this material is not opaque (glass, ice).
*/
def isTransparent: Boolean
/**
* Return the localised name of this material (normally the block name)
*/
def getLocalizedName: String
/** Return the light level emitted by this material (glowstone)
*/
def getLightValue: Int
/**
* Get the item that this material is cut from (full block -> slabs)
*/
def getItem: ItemStack
/** Return the strength of this material
*/
def getStrength(player: EntityPlayer): Float
/**
* Get the strength of saw requried to cut this material
*/
def getCutterStrength: Int
/** Return the localised name of this material (normally the block name)
*/
def getLocalizedName: String
/**
* Get the breaking/walking sound
*/
def getSound: SoundType
/** Get the item that this material is cut from (full block -> slabs)
*/
def getItem: ItemStack
/**
* Return true if this material is solid and opaque (can run wires on etc)
*/
def isSolid = !isTransparent
/** Get the strength of saw requried to cut this material
*/
def getCutterStrength: Int
/**
* Get the explosion resistance of this part to an explosion caused by entity
*/
def explosionResistance(entity: Entity): Float
/** Get the breaking/walking sound
*/
def getSound: SoundType
/** Return true if this material is solid and opaque (can run wires on etc)
*/
def isSolid = !isTransparent
/** Get the explosion resistance of this part to an explosion caused by
* entity
*/
def explosionResistance(entity: Entity): Float
}
/** Interface for overriding the default micro placement highlight handler to
* show the effect of placement on a certain block/part
*/
trait IMicroHighlightRenderer {
/** Return true if a custom highlight was rendered and the default should be
* skipped
*/
def renderHighlight(
player: EntityPlayer,
hit: MovingObjectPosition,
mcrClass: CommonMicroClass,
size: Int,
material: Int
): Boolean
}
private val typeMap = HashMap[String, IMicroMaterial]()
private val nameMap = HashMap[String, Int]()
private var idMap: Array[(String, IMicroMaterial)] = _
private val idWriter = new IDWriter
private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]()
private var maxCuttingStrength: Int = _
private val remap = HashMap[String, String]()
/** Register a micro material with unique identifier name
*/
def registerMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException(
"You must register your materials in the init methods."
)
if (typeMap.contains(name)) {
logger.error("Material with id " + name + " is already registered.")
return
}
/**
* Interface for overriding the default micro placement highlight handler to show the effect of placement on a certain block/part
*/
trait IMicroHighlightRenderer
{
/**
* Return true if a custom highlight was rendered and the default should be skipped
*/
def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: CommonMicroClass, size: Int, material: Int): Boolean
typeMap.put(name, material)
}
/** Replace a micro material with unique identifier name
*/
def replaceMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException(
"You must register your materials in the init methods."
)
if (typeMap.remove(name).isEmpty)
logger.error("Material with id " + name + " was not registered.")
logger.debug("Replaced micro material: " + name)
typeMap.put(name, material)
}
/** Registers a highlight renderer
*/
def registerHighlightRenderer(handler: IMicroHighlightRenderer) {
highlightRenderers += handler
}
def remapName(oldName: String, newName: String): Unit =
remap.put(oldName, newName)
private[microblock] def setupIDMap() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[microblock] def calcMaxCuttingStrength() {
val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]]
maxCuttingStrength = it.flatMap {
case saw: Saw => Some(saw.getMaxCuttingStrength)
case _ => None
}.max
}
private[microblock] def loadIcons() {
if (idMap != null)
idMap.foreach(e => e._2.loadIcons())
}
def getMaxCuttingStrength = maxCuttingStrength
def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.size)
idMap.foreach(e => packet.writeString(e._1))
}
def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
def writeMaterialID(data: MCDataOutput, id: Int) {
idWriter.write(data, id)
}
def readMaterialID(data: MCDataInput) = idWriter.read(data)
def materialName(id: Int) = idMap(id)._1
def materialID(name: String) =
nameMap.get(remap.getOrElse(name, name)) match {
case Some(v) => v
case None =>
logger.error("Missing mapping for part with ID: " + name)
0
}
private val typeMap = HashMap[String, IMicroMaterial]()
private val nameMap = HashMap[String, Int]()
private var idMap: Array[(String, IMicroMaterial)] = _
private val idWriter = new IDWriter
def getMaterial(name: String) =
typeMap.getOrElse(remap.getOrElse(name, name), null)
private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]()
private var maxCuttingStrength: Int = _
def getMaterial(id: Int) = idMap(id)._2
private val remap = HashMap[String, String]()
def getIdMap = idMap
/**
* Register a micro material with unique identifier name
*/
def registerMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException("You must register your materials in the init methods.")
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
if (typeMap.contains(name)) {
logger.error("Material with id " + name + " is already registered.")
return
}
typeMap.put(name, material)
}
/**
* Replace a micro material with unique identifier name
*/
def replaceMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException("You must register your materials in the init methods.")
if (typeMap.remove(name).isEmpty)
logger.error("Material with id " + name + " was not registered.")
logger.debug("Replaced micro material: " + name)
typeMap.put(name, material)
}
/**
* Registers a highlight renderer
*/
def registerHighlightRenderer(handler: IMicroHighlightRenderer) {
highlightRenderers += handler
}
def remapName(oldName:String, newName:String):Unit = remap.put(oldName, newName)
private[microblock] def setupIDMap() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[microblock] def calcMaxCuttingStrength() {
val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]]
maxCuttingStrength = it.flatMap {
case saw: Saw => Some(saw.getMaxCuttingStrength)
case _ => None
}.max
}
private[microblock] def loadIcons() {
if(idMap != null)
idMap.foreach(e => e._2.loadIcons())
}
def getMaxCuttingStrength = maxCuttingStrength
def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.size)
idMap.foreach(e => packet.writeString(e._1))
}
def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
def writeMaterialID(data: MCDataOutput, id: Int) {
idWriter.write(data, id)
}
def readMaterialID(data: MCDataInput) = idWriter.read(data)
def materialName(id: Int) = idMap(id)._1
def materialID(name: String) = nameMap.get(remap.getOrElse(name, name)) match {
case Some(v) => v
case None =>
logger.error("Missing mapping for part with ID: " + name)
0
}
def getMaterial(name: String) = typeMap.getOrElse(remap.getOrElse(name, name), null)
def getMaterial(id: Int) = idMap(id)._2
def getIdMap = idMap
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
MicroblockRender.renderHighlight(player, hit, mcrClass, size, material)
return true
}
}
MicroblockRender.renderHighlight(player, hit, mcrClass, size, material)
return true
}
}
@@ -7,221 +7,241 @@ import net.minecraft.item.ItemStack
import codechicken.microblock.handler.MicroblockProxy._
import net.minecraftforge.oredict.RecipeSorter
object MicroRecipe extends IRecipe
{
RecipeSorter.register("fmp:micro", getClass, RecipeSorter.Category.SHAPED, "after:forge:shapelessore")
object MicroRecipe extends IRecipe {
RecipeSorter.register(
"fmp:micro",
getClass,
RecipeSorter.Category.SHAPED,
"after:forge:shapelessore"
)
def getRecipeOutput = ItemMicroPart.create(1, 1, "tile.stone")
def getRecipeSize = 9
def matches(icraft:InventoryCrafting, world:World) = getCraftingResult(icraft) != null
def getCraftingResult(icraft:InventoryCrafting):ItemStack =
{
var res = getHollowResult(icraft)
if(res != null) return res
res = getGluingResult(icraft)
if(res != null) return res
res = getThinningResult(icraft)
if(res != null) return res
res = getSplittingResult(icraft)
if(res != null) return res
res = getHollowFillResult(icraft)
return res
def getRecipeOutput = ItemMicroPart.create(1, 1, "tile.stone")
def getRecipeSize = 9
def matches(icraft: InventoryCrafting, world: World) =
getCraftingResult(icraft) != null
def getCraftingResult(icraft: InventoryCrafting): ItemStack = {
var res = getHollowResult(icraft)
if (res != null) return res
res = getGluingResult(icraft)
if (res != null) return res
res = getThinningResult(icraft)
if (res != null) return res
res = getSplittingResult(icraft)
if (res != null) return res
res = getHollowFillResult(icraft)
return res
}
def create(
amount: Int,
mcrClass: Int,
size: Int,
material: Int
): ItemStack = {
if (size == 8) {
val item = MicroMaterialRegistry.getMaterial(material).getItem.copy
item.stackSize = amount
return item
}
def create(amount:Int, mcrClass:Int, size:Int, material:Int):ItemStack =
{
if(size == 8)
{
val item = MicroMaterialRegistry.getMaterial(material).getItem.copy
item.stackSize = amount
return item
return ItemMicroPart.create(
amount,
mcrClass << 8 | size,
MicroMaterialRegistry.materialName(material)
)
}
def microMaterial(item: ItemStack) =
if (item.getItem == itemMicro)
ItemMicroPart.getMaterialID(item)
else
findMaterial(item)
def microClass(item: ItemStack) =
if (item.getItem == itemMicro)
item.getItemDamage >> 8
else
0
def microSize(item: ItemStack) =
if (item.getItem == itemMicro)
item.getItemDamage & 0xff
else
8
def getHollowResult(icraft: InventoryCrafting): ItemStack = {
if (icraft.getStackInRowAndColumn(1, 1) != null) return null
val first = icraft.getStackInRowAndColumn(0, 0)
if (first == null || first.getItem != itemMicro || microClass(first) != 0)
return null
val size = microSize(first)
val material = microMaterial(first)
for (i <- 1 to 8 if i != 4) {
val item = icraft.getStackInSlot(i)
if (
item == null || item.getItem != itemMicro ||
microMaterial(
item
) != material || item.getItemDamage != first.getItemDamage
)
return null
}
return create(8, 1, size, material)
}
def getGluingResult(icraft: InventoryCrafting): ItemStack = {
var size = 0
var count = 0
var smallest = 0
var mcrClass = 0
var material = 0
for (i <- 0 until 9) {
val item = icraft.getStackInSlot(i)
if (item != null) {
if (item.getItem != itemMicro) return null
if (count == 0) {
size = microSize(item)
mcrClass = microClass(item)
material = microMaterial(item)
count = 1
smallest = size
} else if (
microClass(item) != mcrClass || microMaterial(item) != material
) return null
else if (mcrClass >= 2 && microSize(item) != smallest) return null
else {
smallest = Math.min(smallest, microSize(item))
count += 1
size += microSize(item)
}
return ItemMicroPart.create(amount, mcrClass<<8|size, MicroMaterialRegistry.materialName(material))
}
}
def microMaterial(item:ItemStack) =
if(item.getItem == itemMicro)
ItemMicroPart.getMaterialID(item)
if (count <= 1) return null
mcrClass match {
case 3 =>
count match {
case 2 => create(1, 0, smallest, material)
case _ => null
}
case 2 =>
count match {
case 2 => create(1, 3, smallest, material)
case 4 => create(1, 0, smallest, material)
case _ => null
}
case 1 | 0 =>
val base = Seq(1, 2, 4).find(s => (s & size) != 0)
if (base.isEmpty)
create(size / 8, 0, 8, material)
else if (base.get <= smallest)
null
else
findMaterial(item)
def microClass(item:ItemStack) =
if(item.getItem == itemMicro)
item.getItemDamage >> 8
else
0
def microSize(item:ItemStack) =
if(item.getItem == itemMicro)
item.getItemDamage & 0xFF
else
8
def getHollowResult(icraft:InventoryCrafting):ItemStack =
{
if(icraft.getStackInRowAndColumn(1, 1) != null) return null
val first = icraft.getStackInRowAndColumn(0, 0)
if(first == null || first.getItem != itemMicro || microClass(first) != 0) return null
val size = microSize(first)
val material = microMaterial(first)
for(i <- 1 to 8 if i != 4)
{
val item = icraft.getStackInSlot(i)
if(item == null || item.getItem != itemMicro ||
microMaterial(item) != material || item.getItemDamage != first.getItemDamage)
return null
}
return create(8, 1, size, material)
create(size / base.get, mcrClass, base.get, material)
case _ => null
}
def getGluingResult(icraft:InventoryCrafting):ItemStack =
{
var size = 0
var count = 0
var smallest = 0
var mcrClass = 0
var material = 0
for(i <- 0 until 9)
{
val item = icraft.getStackInSlot(i)
if(item != null)
{
if(item.getItem != itemMicro) return null
if(count == 0)
{
size = microSize(item)
mcrClass = microClass(item)
material = microMaterial(item)
count = 1
smallest = size
}
else if(microClass(item) != mcrClass || microMaterial(item) != material) return null
else if(mcrClass >= 2 && microSize(item) != smallest) return null
else
{
smallest= Math.min(smallest, microSize(item))
count+=1
size+=microSize(item)
}
}
}
if(count <= 1) return null
mcrClass match {
case 3 => count match {
case 2 => create(1, 0, smallest, material)
case _ => null
}
case 2 => count match {
case 2 => create(1, 3, smallest, material)
case 4 => create(1, 0, smallest, material)
case _ => null
}
case 1|0 =>
val base = Seq(1, 2, 4).find(s => (s&size) != 0)
if(base.isEmpty)
create(size/8, 0, 8, material)
else if(base.get <= smallest)
null
else
create(size/base.get, mcrClass, base.get, material)
case _ => null
}
}
def getSaw(icraft: InventoryCrafting): (Saw, Int, Int) = {
for (r <- 0 until 3)
for (c <- 0 until 3) {
val item = icraft.getStackInRowAndColumn(c, r)
if (item != null && item.getItem.isInstanceOf[Saw])
return (item.getItem.asInstanceOf[Saw], r, c)
}
return (null, 0, 0)
}
def canCut(saw: Saw, sawItem: ItemStack, material: Int): Boolean = {
val sawStrength = saw.getCuttingStrength(sawItem)
val matStrength =
MicroMaterialRegistry.getMaterial(material).getCutterStrength
return sawStrength >= matStrength || sawStrength == MicroMaterialRegistry.getMaxCuttingStrength
}
def getThinningResult(icraft: InventoryCrafting): ItemStack = {
val (saw, row, col) = getSaw(icraft)
if (saw == null)
return null
val item = icraft.getStackInRowAndColumn(col, row + 1)
if (item == null)
return null
val size = microSize(item)
val material = microMaterial(item)
val mcrClass = microClass(item)
if (
size == 1 || material < 0 || !canCut(
saw,
icraft.getStackInRowAndColumn(col, row),
material
)
)
return null
for (r <- 0 until 3)
for (c <- 0 until 3)
if (
(c != col || r != row && r != row + 1) &&
icraft.getStackInRowAndColumn(c, r) != null
)
return null
return create(2, mcrClass, size / 2, material)
}
def findMaterial(item: ItemStack): Int =
MicroMaterialRegistry.getIdMap.find { m =>
val mitem = m._2.getItem
item.getItem == mitem.getItem &&
item.getItemDamage == mitem.getItemDamage &&
ItemStack.areItemStackTagsEqual(item, mitem)
} match {
case None => -1
case Some((name, m)) => MicroMaterialRegistry.materialID(name)
}
def getSaw(icraft:InventoryCrafting):(Saw, Int, Int) =
{
for(r <- 0 until 3)
for(c <- 0 until 3)
{
val item = icraft.getStackInRowAndColumn(c, r)
if(item != null && item.getItem.isInstanceOf[Saw])
return (item.getItem.asInstanceOf[Saw], r, c)
}
return (null, 0, 0)
val splitMap = Map(0 -> 3, 1 -> 3, 3 -> 2)
def getSplittingResult(icraft: InventoryCrafting): ItemStack = {
val (saw, row, col) = getSaw(icraft)
if (saw == null) return null
val item = icraft.getStackInRowAndColumn(col + 1, row)
if (item == null || item.getItem != itemMicro) return null
val mcrClass = microClass(item)
val material = microMaterial(item)
if (!canCut(saw, icraft.getStackInRowAndColumn(col, row), material))
return null
val split = splitMap.get(mcrClass)
if (split.isEmpty) return null
for (r <- 0 until 3)
for (c <- 0 until 3)
if (
(r != row || c != col && c != col + 1) &&
icraft.getStackInRowAndColumn(c, r) != null
)
return null
return create(2, split.get, microSize(item), material)
}
def getHollowFillResult(icraft: InventoryCrafting): ItemStack = {
var cover: ItemStack = null
for (i <- 0 until 9) {
val item = icraft.getStackInSlot(i)
if (item != null) {
if (item.getItem != itemMicro || cover != null || microClass(item) != 1)
return null
else cover = item
}
}
def canCut(saw:Saw, sawItem:ItemStack, material:Int):Boolean = {
val sawStrength = saw.getCuttingStrength(sawItem)
val matStrength = MicroMaterialRegistry.getMaterial(material).getCutterStrength
return sawStrength >= matStrength || sawStrength == MicroMaterialRegistry.getMaxCuttingStrength
}
def getThinningResult(icraft:InventoryCrafting):ItemStack =
{
val (saw, row, col) = getSaw(icraft)
if(saw == null)
return null
val item = icraft.getStackInRowAndColumn(col, row+1)
if(item == null)
return null
val size = microSize(item)
val material = microMaterial(item)
val mcrClass = microClass(item)
if(size == 1 || material < 0 || !canCut(saw, icraft.getStackInRowAndColumn(col, row), material))
return null
for(r <- 0 until 3)
for(c <- 0 until 3)
if((c != col || r != row && r != row+1) &&
icraft.getStackInRowAndColumn(c, r) != null)
return null
return create(2, mcrClass, size/2, material)
}
def findMaterial(item:ItemStack):Int =
MicroMaterialRegistry.getIdMap.find{m => val mitem = m._2.getItem
item.getItem == mitem.getItem &&
item.getItemDamage == mitem.getItemDamage &&
ItemStack.areItemStackTagsEqual(item, mitem)} match {
case None => -1
case Some((name, m)) => MicroMaterialRegistry.materialID(name)
}
val splitMap = Map(0 -> 3, 1 -> 3, 3 -> 2)
def getSplittingResult(icraft:InventoryCrafting):ItemStack =
{
val (saw, row, col) = getSaw(icraft)
if(saw == null) return null
val item = icraft.getStackInRowAndColumn(col+1, row)
if(item == null || item.getItem != itemMicro) return null
val mcrClass = microClass(item)
val material = microMaterial(item)
if(!canCut(saw, icraft.getStackInRowAndColumn(col, row), material)) return null
val split = splitMap.get(mcrClass)
if(split.isEmpty)return null
for(r <- 0 until 3)
for(c <- 0 until 3)
if((r != row || c != col && c != col+1) &&
icraft.getStackInRowAndColumn(c, r) != null)
return null
return create(2, split.get, microSize(item), material)
}
def getHollowFillResult(icraft:InventoryCrafting):ItemStack =
{
var cover:ItemStack = null
for(i <- 0 until 9)
{
val item = icraft.getStackInSlot(i)
if(item != null)
{
if(item.getItem != itemMicro || cover != null || microClass(item) != 1) return null
else cover = item
}
}
if(cover == null) return null
return create(1, 0, microSize(cover), microMaterial(cover))
}
}
if (cover == null) return null
return create(1, 0, microSize(cover), microMaterial(cover))
}
}
@@ -31,128 +31,147 @@ import net.minecraft.init.Blocks
import cpw.mods.fml.relauncher.{Side, SideOnly}
import codechicken.lib.render.BlockRenderer.BlockFace
abstract class Microblock(var material:Int = 0) extends TCuboidPart
{
var shape:Byte = 0
abstract class Microblock(var material: Int = 0) extends TCuboidPart {
var shape: Byte = 0
def microClass:MicroblockClass
def microClass: MicroblockClass
def getType = microClass.getName
def getType = microClass.getName
override def getStrength(hit:MovingObjectPosition, player:EntityPlayer) = getIMaterial match {
case null => super.getStrength(hit, player)
case mat => mat.getStrength(player)
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
override def doesTick = false
def setShape(size:Int, slot:Int) = shape = (size<<4|slot).toByte
def getSize = shape >> 4
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))
def getShape = shape & 0xf
return null//unreachable
}
override def writeDesc(packet:MCDataOutput) {
writeMaterialID(packet, material)
packet.writeByte(shape)
}
override def readDesc(packet:MCDataInput) {
shape = packet.readByte
}
def setShape(size: Int, slot: Int) = shape = (size << 4 | slot).toByte
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
def getMaterial = material
override def explosionResistance(entity:Entity) = getIMaterial.explosionResistance(entity) * microClass.getResistanceFactor
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
{
def getBrokenIcon(side:Int) = getIMaterial match {
case null => Blocks.stone.getIcon(0, 0)
case mat => mat.getBreakingIcon(side)
}
trait MicroblockClient
extends Microblock
with TIconHitEffects
with IMicroMaterialRender {
def getBrokenIcon(side: Int) = getIMaterial match {
case null => Blocks.stone.getIcon(0, 0)
case mat => mat.getBreakingIcon(side)
}
override def renderStatic(pos:Vector3, pass:Int) = {
if(getIMaterial.canRenderInPass(pass)) {
render(pos, pass)
true
}
else
false
}
override def renderStatic(pos: Vector3, pass: Int) = {
if (getIMaterial.canRenderInPass(pass)) {
render(pos, pass)
true
} else
false
}
def render(pos:Vector3, pass:Int)
def render(pos: Vector3, pass: Int)
override def getRenderBounds = getBounds
override def getRenderBounds = getBounds
}
trait CommonMicroblockClient extends CommonMicroblock with MicroblockClient with TMicroOcclusionClient
{
def render(pos:Vector3, pass:Int) {
if(pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
}
trait CommonMicroblockClient
extends CommonMicroblock
with MicroblockClient
with TMicroOcclusionClient {
def render(pos: Vector3, pass: Int) {
if (pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else
MicroblockRender.renderCuboid(
pos,
getIMaterial,
pass,
renderBounds,
renderMask
)
}
}
trait CommonMicroblock extends Microblock with JPartialOcclusion with TMicroOcclusion with TSlottedPart
{
def microClass:CommonMicroClass
trait CommonMicroblock
extends Microblock
with JPartialOcclusion
with TMicroOcclusion
with TSlottedPart {
def microClass: CommonMicroClass
def getSlot = getShape
def getSlotMask = 1<<getSlot
def getPartialOcclusionBoxes = Seq(getBounds)
override def itemClassID = microClass.getClassId
}
def getSlot = getShape
def getSlotMask = 1 << getSlot
def getPartialOcclusionBoxes = Seq(getBounds)
override def itemClassID = microClass.getClassId
}
@@ -6,55 +6,58 @@ import net.minecraft.nbt.NBTTagCompound
import codechicken.lib.data.MCDataInput
import cpw.mods.fml.relauncher.{Side, SideOnly}
abstract class MicroblockClass extends IPartFactory2
{
def getName:String
def baseTrait:Class[_ <: Microblock]
@SideOnly(Side.CLIENT)
def clientTrait:Class[_ <: MicroblockClient]
def getResistanceFactor:Float
abstract class MicroblockClass extends IPartFactory2 {
def getName: String
def baseTrait: Class[_ <: Microblock]
@SideOnly(Side.CLIENT)
def clientTrait: Class[_ <: MicroblockClient]
def getResistanceFactor: Float
val baseTraitId = MicroblockGenerator.registerTrait(baseTrait)
@SideOnly(Side.CLIENT)
lazy val clientTraitId = MicroblockGenerator.registerTrait(clientTrait)
val baseTraitId = MicroblockGenerator.registerTrait(baseTrait)
@SideOnly(Side.CLIENT)
lazy val clientTraitId = MicroblockGenerator.registerTrait(clientTrait)
def register() {
MultiPartRegistry.registerParts(this, getName)
}
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")))
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 = _
/** Microblocks with corresponding items
*/
abstract class CommonMicroClass extends MicroblockClass {
private var classId: Int = _
def getClassId = classId
def itemSlot:Int
def placementProperties:PlacementProperties
def getClassId = classId
def itemSlot: Int
def placementProperties: PlacementProperties
def register(id:Int) {
register()
classId = id
CommonMicroClass.registerMicroClass(this, id)
}
def register(id: Int) {
register()
classId = id
CommonMicroClass.registerMicroClass(this, id)
}
}
object CommonMicroClass
{
val classes = new Array[CommonMicroClass](256)
object CommonMicroClass {
val classes = new Array[CommonMicroClass](256)
def getMicroClass(modelId:Int) = classes(modelId>>8)
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)
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
}
}
classes(id) = mcrClass
}
}
@@ -3,23 +3,24 @@ package codechicken.microblock
import codechicken.multipart.asm.{ScratchBitSet, ASMMixinFactory}
import java.util.BitSet
object MicroblockGenerator extends ASMMixinFactory(classOf[Microblock], classOf[Int]) with ScratchBitSet
{
trait IGeneratedMaterial
{
def addTraits(traits:BitSet, mcrClass:MicroblockClass, client:Boolean)
object MicroblockGenerator
extends ASMMixinFactory(classOf[Microblock], classOf[Int])
with ScratchBitSet {
trait IGeneratedMaterial {
def addTraits(traits: BitSet, mcrClass: MicroblockClass, client: Boolean)
}
def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = {
val bitset = freshBitSet
bitset.set(mcrClass.baseTraitId)
if (client) bitset.set(mcrClass.clientTraitId)
MicroMaterialRegistry.getMaterial(material) match {
case genMat: IGeneratedMaterial =>
genMat.addTraits(bitset, mcrClass, client)
case _ =>
}
def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = {
val bitset = freshBitSet
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)
}
construct(bitset, material: Integer)
}
}
@@ -11,157 +11,170 @@ import net.minecraft.item.ItemStack
import codechicken.multipart.TileMultipart
import codechicken.lib.raytracer.ExtendedMOP
abstract class ExecutablePlacement(val pos:BlockCoord, val part:Microblock)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
def consume(world:World, player:EntityPlayer, item:ItemStack)
abstract class ExecutablePlacement(val pos: BlockCoord, val part: Microblock) {
def place(world: World, player: EntityPlayer, item: ItemStack)
def consume(world: World, player: EntityPlayer, item: ItemStack)
}
class AdditionPlacement($pos:BlockCoord, $part:Microblock) extends ExecutablePlacement($pos, $part)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
{
TileMultipart.addPart(world, pos, part)
class AdditionPlacement($pos: BlockCoord, $part: Microblock)
extends ExecutablePlacement($pos, $part) {
def place(world: World, player: EntityPlayer, item: ItemStack) {
TileMultipart.addPart(world, pos, part)
}
def consume(world: World, player: EntityPlayer, item: ItemStack) {
item.stackSize -= 1
}
}
class ExpandingPlacement($pos: BlockCoord, $part: Microblock, opart: Microblock)
extends ExecutablePlacement($pos, $part) {
def place(world: World, player: EntityPlayer, item: ItemStack) {
opart.shape = part.shape
opart.tile.notifyPartChange(opart)
opart.sendShapeUpdate()
}
def consume(world: World, player: EntityPlayer, item: ItemStack) {
item.stackSize -= 1
}
}
abstract class PlacementProperties {
def opposite(slot: Int, side: Int): Int
def sneakOpposite(slot: Int, side: Int) = true
def expand(slot: Int, side: Int) = true
def microClass: MicroblockClass
def placementGrid: PlacementGrid
def customPlacement(pmt: MicroblockPlacement): ExecutablePlacement = null
}
object MicroblockPlacement {
def apply(
player: EntityPlayer,
hit: MovingObjectPosition,
size: Int,
material: Int,
checkMaterial: Boolean,
pp: PlacementProperties
): ExecutablePlacement =
new MicroblockPlacement(player, hit, size, material, checkMaterial, pp)()
}
class MicroblockPlacement(
val player: EntityPlayer,
val hit: MovingObjectPosition,
val size: Int,
val material: Int,
val checkMaterial: Boolean,
val pp: PlacementProperties
) {
val world = player.worldObj
val mcrClass = pp.microClass
val pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ)
val vhit = new Vector3(hit.hitVec).add(-pos.x, -pos.y, -pos.z)
val gtile = TileMultipart.getOrConvertTile2(world, pos)
val htile = gtile._1
val slot = pp.placementGrid.getHitSlot(vhit, hit.sideHit)
val oslot = pp.opposite(slot, hit.sideHit)
val d = getHitDepth(vhit, hit.sideHit)
val useOppMod = pp.sneakOpposite(slot, hit.sideHit)
val oppMod = player.isControlDown
val internal = d < 1 && htile != null
val doExpand =
internal && !gtile._2 && !player.isSneaking && !(oppMod && useOppMod) && pp
.expand(slot, hit.sideHit)
val side = hit.sideHit
def apply(): ExecutablePlacement = {
val customPlacement = pp.customPlacement(this)
if (customPlacement != null)
return customPlacement
if (slot < 0)
return null
if (doExpand) {
val hpart = htile.partList(ExtendedMOP.getData[(Int, _)](hit)._1)
if (hpart.getType == mcrClass.getName) {
val mpart = hpart.asInstanceOf[CommonMicroblock]
if (mpart.material == material && mpart.getSize + size < 8)
return expand(mpart)
}
}
def consume(world:World, player:EntityPlayer, item:ItemStack)
{
item.stackSize-=1
}
}
class ExpandingPlacement($pos:BlockCoord, $part:Microblock, opart:Microblock) extends ExecutablePlacement($pos, $part)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
{
opart.shape = part.shape
opart.tile.notifyPartChange(opart)
opart.sendShapeUpdate()
}
def consume(world:World, player:EntityPlayer, item:ItemStack)
{
item.stackSize-=1
}
}
abstract class PlacementProperties
{
def opposite(slot:Int, side:Int):Int
def sneakOpposite(slot:Int, side:Int) = true
def expand(slot:Int, side:Int) = true
def microClass:MicroblockClass
def placementGrid:PlacementGrid
def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement = null
}
object MicroblockPlacement
{
def apply(player:EntityPlayer, hit:MovingObjectPosition, size:Int, material:Int, checkMaterial:Boolean, pp:PlacementProperties):ExecutablePlacement =
new MicroblockPlacement(player, hit, size, material, checkMaterial, pp)()
}
class MicroblockPlacement(val player:EntityPlayer, val hit:MovingObjectPosition, val size:Int, val material:Int, val checkMaterial:Boolean, val pp:PlacementProperties)
{
val world = player.worldObj
val mcrClass = pp.microClass
val pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ)
val vhit = new Vector3(hit.hitVec).add(-pos.x, -pos.y, -pos.z)
val gtile = TileMultipart.getOrConvertTile2(world, pos)
val htile = gtile._1
val slot = pp.placementGrid.getHitSlot(vhit, hit.sideHit)
val oslot = pp.opposite(slot, hit.sideHit)
val d = getHitDepth(vhit, hit.sideHit)
val useOppMod = pp.sneakOpposite(slot, hit.sideHit)
val oppMod = player.isControlDown
val internal = d < 1 && htile != null
val doExpand = internal && !gtile._2 && !player.isSneaking && !(oppMod && useOppMod) && pp.expand(slot, hit.sideHit)
val side = hit.sideHit
def apply():ExecutablePlacement =
{
val customPlacement = pp.customPlacement(this)
if(customPlacement != null)
return customPlacement
if(slot < 0)
return null
if(doExpand)
{
val hpart = htile.partList(ExtendedMOP.getData[(Int, _)](hit)._1)
if(hpart.getType == mcrClass.getName)
{
val mpart = hpart.asInstanceOf[CommonMicroblock]
if(mpart.material == material && mpart.getSize + size < 8)
return expand(mpart)
}
}
if(internal)
{
if(d < 0.5 || !useOppMod)
{
val ret = internalPlacement(htile, slot)
if(ret != null) {
if(!useOppMod || !oppMod) return ret
else return internalPlacement(htile, oslot)
}
}
if(useOppMod && !oppMod)
return internalPlacement(htile, oslot)
else
return externalPlacement(slot)
if (internal) {
if (d < 0.5 || !useOppMod) {
val ret = internalPlacement(htile, slot)
if (ret != null) {
if (!useOppMod || !oppMod) return ret
else return internalPlacement(htile, oslot)
}
}
if (useOppMod && !oppMod)
return internalPlacement(htile, oslot)
else
return externalPlacement(slot)
}
if(!useOppMod || !oppMod)
return externalPlacement(slot)
else
return externalPlacement(oslot)
}
def expand(mpart:CommonMicroblock):ExecutablePlacement = expand(mpart, create(mpart.getSize+size, mpart.getSlot, mpart.material))
def expand(mpart:Microblock, npart:Microblock):ExecutablePlacement =
{
val pos = new BlockCoord(mpart.tile)
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && mpart.tile.canReplacePart(mpart, npart))
return new ExpandingPlacement(pos, npart, mpart)
return null
}
def internalPlacement(htile:TileMultipart, slot:Int):ExecutablePlacement = internalPlacement(htile, create(size, slot, material))
def internalPlacement(htile:TileMultipart, npart:Microblock):ExecutablePlacement =
{
val pos = new BlockCoord(htile)
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && htile.canAddPart(npart))
return new AdditionPlacement(pos, npart)
return null
}
def externalPlacement(slot:Int):ExecutablePlacement = externalPlacement(create(size, slot, material))
def externalPlacement(npart:Microblock):ExecutablePlacement =
{
val pos = this.pos.copy().offset(side)
if(TileMultipart.canPlacePart(world, pos, npart))
return new AdditionPlacement(pos, npart)
return null
}
def getHitDepth(vhit:Vector3, side:Int):Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
def create(size:Int, slot:Int, material:Int) = {
val part = mcrClass.create(world.isRemote, material)
part.setShape(size, slot)
part
}
}
if (!useOppMod || !oppMod)
return externalPlacement(slot)
else
return externalPlacement(oslot)
}
def expand(mpart: CommonMicroblock): ExecutablePlacement =
expand(mpart, create(mpart.getSize + size, mpart.getSlot, mpart.material))
def expand(mpart: Microblock, npart: Microblock): ExecutablePlacement = {
val pos = new BlockCoord(mpart.tile)
if (
TileMultipart.checkNoEntityCollision(world, pos, npart) && mpart.tile
.canReplacePart(mpart, npart)
)
return new ExpandingPlacement(pos, npart, mpart)
return null
}
def internalPlacement(htile: TileMultipart, slot: Int): ExecutablePlacement =
internalPlacement(htile, create(size, slot, material))
def internalPlacement(
htile: TileMultipart,
npart: Microblock
): ExecutablePlacement = {
val pos = new BlockCoord(htile)
if (
TileMultipart.checkNoEntityCollision(world, pos, npart) && htile
.canAddPart(npart)
)
return new AdditionPlacement(pos, npart)
return null
}
def externalPlacement(slot: Int): ExecutablePlacement = externalPlacement(
create(size, slot, material)
)
def externalPlacement(npart: Microblock): ExecutablePlacement = {
val pos = this.pos.copy().offset(side)
if (TileMultipart.canPlacePart(world, pos, npart))
return new AdditionPlacement(pos, npart)
return null
}
def getHitDepth(vhit: Vector3, side: Int): Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side % 2 ^ 1)
def create(size: Int, slot: Int, material: Int) = {
val part = mcrClass.create(world.isRemote, material)
part.setShape(size, slot)
part
}
}
@@ -8,48 +8,64 @@ 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)
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]
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)
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)
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()
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)
}
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)
}
}
}
@@ -7,173 +7,174 @@ import Rotation._
import org.lwjgl.opengl.GL11._
import codechicken.multipart.PartMap
trait PlacementGrid
{
def getHitSlot(vhit: Vector3, side: Int): Int
trait PlacementGrid {
def getHitSlot(vhit: Vector3, side: Int): Int
def render(hit: Vector3, side: Int) {
glTransformFace(hit, side)
glLineWidth(2)
glColor4f(0, 0, 0, 1)
glBegin(GL_LINES)
drawLines()
glEnd()
glPopMatrix()
}
def render(hit: Vector3, side: Int) {
glTransformFace(hit, side)
glLineWidth(2)
glColor4f(0, 0, 0, 1)
glBegin(GL_LINES)
drawLines()
glEnd()
glPopMatrix()
}
def drawLines() {}
def drawLines() {}
def glTransformFace(hit: Vector3, side: Int) {
val pos = new BlockCoord(hit)
glPushMatrix()
glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
sideRotations(side).glApply()
val rhit = new Vector3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5).subtract(hit).apply(sideRotations(side ^ 1).inverse)
glTranslated(0, rhit.y - 0.002, 0)
}
def glTransformFace(hit: Vector3, side: Int) {
val pos = new BlockCoord(hit)
glPushMatrix()
glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
sideRotations(side).glApply()
val rhit = new Vector3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
.subtract(hit)
.apply(sideRotations(side ^ 1).inverse)
glTranslated(0, rhit.y - 0.002, 0)
}
}
class FaceEdgeGrid(size: Double) extends PlacementGrid
{
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
class FaceEdgeGrid(size: Double) extends PlacementGrid {
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(size, 0, size)
glVertex3d(0.5, 0, 0.5)
glVertex3d(size, 0, size)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(-size, 0, size)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(-size, 0, size)
glVertex3d(0.5, 0, -0.5)
glVertex3d(size, 0, -size)
glVertex3d(0.5, 0, -0.5)
glVertex3d(size, 0, -size)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-size, 0, -size)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-size, 0, -size)
glVertex3d(-size, 0, -size)
glVertex3d(-size, 0, size)
glVertex3d(-size, 0, -size)
glVertex3d(-size, 0, size)
glVertex3d(-size, 0, size)
glVertex3d(size, 0, size)
glVertex3d(-size, 0, size)
glVertex3d(size, 0, size)
glVertex3d(size, 0, size)
glVertex3d(size, 0, -size)
glVertex3d(size, 0, size)
glVertex3d(size, 0, -size)
glVertex3d(size, 0, -size)
glVertex3d(-size, 0, -size)
}
glVertex3d(size, 0, -size)
glVertex3d(-size, 0, -size)
}
def getHitSlot(vhit: Vector3, side: Int) = {
val s1 = (side + 2) % 6
val s2 = (side + 4) % 6
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
def getHitSlot(vhit: Vector3, side: Int) = {
val s1 = (side + 2) % 6
val s2 = (side + 4) % 6
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
if (Math.abs(u) < size && Math.abs(v) < size)
side ^ 1
else if (Math.abs(u) > Math.abs(v))
if (u > 0) s1 else s1 ^ 1
else
if (v > 0) s2 else s2 ^ 1
}
if (Math.abs(u) < size && Math.abs(v) < size)
side ^ 1
else if (Math.abs(u) > Math.abs(v))
if (u > 0) s1 else s1 ^ 1
else if (v > 0) s2
else s2 ^ 1
}
}
object FacePlacementGrid extends FaceEdgeGrid(1 / 4D)
object FacePlacementGrid extends FaceEdgeGrid(1 / 4d)
object CornerPlacementGrid extends PlacementGrid
{
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
object CornerPlacementGrid extends PlacementGrid {
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0, 0, -0.5)
glVertex3d(0, 0, 0.5)
glVertex3d(0, 0, -0.5)
glVertex3d(0, 0, 0.5)
glVertex3d(-0.5, 0, 0)
glVertex3d(0.5, 0, 0)
}
glVertex3d(-0.5, 0, 0)
glVertex3d(0.5, 0, 0)
}
def getHitSlot(vhit: Vector3, side: Int): Int = {
val s1 = ((side & 6) + 3) % 6
val s2 = ((side & 6) + 5) % 6
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
def getHitSlot(vhit: Vector3, side: Int): Int = {
val s1 = ((side & 6) + 3) % 6
val s2 = ((side & 6) + 5) % 6
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
val bu = if (u >= 0) 1 else 0
val bv = if (v >= 0) 1 else 0
val bw = (side & 1) ^ 1
val bu = if (u >= 0) 1 else 0
val bv = if (v >= 0) 1 else 0
val bw = (side & 1) ^ 1
return 7 + (
bw << (side >> 1) |
bu << (s1 >> 1) |
bv << (s2 >> 1))
}
return 7 + (bw << (side >> 1) |
bu << (s1 >> 1) |
bv << (s2 >> 1))
}
}
object EdgePlacementGrid extends PlacementGrid
{
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
object EdgePlacementGrid extends PlacementGrid {
override def drawLines() {
glVertex3d(-0.5, 0, -0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(-0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, 0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0.5, 0, -0.5)
glVertex3d(-0.5, 0, -0.5)
glVertex3d(0.25, 0, -0.5)
glVertex3d(0.25, 0, 0.5)
glVertex3d(0.25, 0, -0.5)
glVertex3d(0.25, 0, 0.5)
glVertex3d(-0.25, 0, -0.5)
glVertex3d(-0.25, 0, 0.5)
glVertex3d(-0.25, 0, -0.5)
glVertex3d(-0.25, 0, 0.5)
glVertex3d(-0.5, 0, 0.25)
glVertex3d(0.5, 0, 0.25)
glVertex3d(-0.5, 0, 0.25)
glVertex3d(0.5, 0, 0.25)
glVertex3d(-0.5, 0, -0.25)
glVertex3d(0.5, 0, -0.25)
}
glVertex3d(-0.5, 0, -0.25)
glVertex3d(0.5, 0, -0.25)
}
override def getHitSlot(vhit: Vector3, side: Int): Int = {
val s1 = (side + 2) % 6
val s2 = (side + 4) % 6
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
override def getHitSlot(vhit: Vector3, side: Int): Int = {
val s1 = (side + 2) % 6
val s2 = (side + 4) % 6
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
if (Math.abs(u) < 4 / 16D && Math.abs(v) < 4 / 16D)
return -1
if (Math.abs(u) < 4 / 16d && Math.abs(v) < 4 / 16d)
return -1
if(Math.abs(u) > 4 / 16D && Math.abs(v) > 4 / 16D)
return PartMap.edgeBetween(if(u > 0) s1 else s1^1, if(v > 0) s2 else s2^1)
if (Math.abs(u) > 4 / 16d && Math.abs(v) > 4 / 16d)
return PartMap.edgeBetween(
if (u > 0) s1 else s1 ^ 1,
if (v > 0) s2 else s2 ^ 1
)
val s = if(Math.abs(u) > Math.abs(v))
if(u > 0) s1 else s1^1
else
if(v > 0) s2 else s2^1
val s =
if (Math.abs(u) > Math.abs(v))
if (u > 0) s1 else s1 ^ 1
else if (v > 0) s2
else s2 ^ 1
return PartMap.edgeBetween(side^1, s)
}
}
return PartMap.edgeBetween(side ^ 1, s)
}
}
@@ -5,257 +5,249 @@ import codechicken.lib.vec.Cuboid6
import codechicken.multipart.PartMap._
import codechicken.lib.data.MCDataInput
/**
* Suite of classes for performing microblock style occlusion and shrink rendering. Use at your own peril.
*/
object MicroOcclusion
{
def shrink(renderBounds:Cuboid6, b:Cuboid6, side:Int) = side match
/** Suite of classes for performing microblock style occlusion and shrink
* rendering. Use at your own peril.
*/
object MicroOcclusion {
def shrink(renderBounds: Cuboid6, b: Cuboid6, side: Int) = side match {
case -1 =>
case 0 => if (renderBounds.min.y < b.max.y) renderBounds.min.y = b.max.y
case 1 => if (renderBounds.max.y > b.min.y) renderBounds.max.y = b.min.y
case 2 => if (renderBounds.min.z < b.max.z) renderBounds.min.z = b.max.z
case 3 => if (renderBounds.max.z > b.min.z) renderBounds.max.z = b.min.z
case 4 => if (renderBounds.min.x < b.max.x) renderBounds.min.x = b.max.x
case 5 => if (renderBounds.max.x > b.min.x) renderBounds.max.x = b.min.x
}
def shrinkFrom(
p: JMicroShrinkRender,
other: JMicroShrinkRender,
renderBounds: Cuboid6
): Int = {
if (shrinkTest(p, other)) {
shrink(
renderBounds,
other.getBounds,
shrinkSide(p.getSlot, other.getSlot)
)
} else if (
other.getSlot < 6 && !other.isTransparent
) // other gets full face, we didn't shrink, flag rendermask
{
if (
other.getSlot match {
case 0 => renderBounds.min.y <= 0
case 1 => renderBounds.max.y >= 1
case 2 => renderBounds.min.z <= 0
case 3 => renderBounds.max.z >= 1
case 4 => renderBounds.min.x <= 0
case 5 => renderBounds.max.x >= 1
}
)
return 1 << other.getSlot
}
return 0
}
def shrink(p: JMicroShrinkRender, renderBounds: Cuboid6, m: Int): Int = {
var renderMask = 0
val part = p.asInstanceOf[TMultiPart]
val tile = part.tile
for (i <- 0 until m)
if (i != p.getSlot)
tile.partMap(i) match {
case other: JMicroShrinkRender =>
renderMask |= shrinkFrom(p, other, renderBounds)
case _ =>
}
return renderMask
}
def shrinkSide(s1: Int, s2: Int): Int = {
if (s2 < 6) // other is a cover
return s2
if (s1 < 15) // both corners
{
val c1 = s1 - 7
val c2 = s2 - 7
return c1 ^ c2 match // different axis bits will become 1, same 0
{
case 1 => c2 & 1
case 2 => 2 | (c2 & 2) >> 1
case 4 => 4 | (c2 & 4) >> 2
case _ => -1
}
}
if (s2 < 15) // edge, other corner
{
val e1 = s1 - 15
val c2 = s2 - 7
val ebits = unpackEdgeBits(e1)
if ((c2 & edgeAxisMask(e1)) != ebits)
return -1
return (e1 & 0xc) >> 1 | (c2 & (~ebits)) >> (e1 >> 2)
}
// both edges
{
case -1 =>
case 0 => if(renderBounds.min.y < b.max.y)renderBounds.min.y = b.max.y
case 1 => if(renderBounds.max.y > b.min.y)renderBounds.max.y = b.min.y
case 2 => if(renderBounds.min.z < b.max.z)renderBounds.min.z = b.max.z
case 3 => if(renderBounds.max.z > b.min.z)renderBounds.max.z = b.min.z
case 4 => if(renderBounds.min.x < b.max.x)renderBounds.min.x = b.max.x
case 5 => if(renderBounds.max.x > b.min.x)renderBounds.max.x = b.min.x
val e1 = s1 - 15
val e2 = s2 - 15
val e1bits = unpackEdgeBits(e1)
val e2bits = unpackEdgeBits(e2)
if ((e1 & 0xc) == (e2 & 0xc)) // same axis
{
return e1bits ^ e2bits match {
case 1 => if ((e2bits & 1) == 0) 0 else 1
case 2 => if ((e2bits & 2) == 0) 2 else 3
case 4 => if ((e2bits & 4) == 0) 4 else 5
case _ => -1
}
} else {
val mask = edgeAxisMask(e1) & edgeAxisMask(e2)
if ((e1bits & mask) != (e2bits & mask))
return -1
return e1 >> 2 match {
case 0 => if ((e2bits & 1) == 0) 0 else 1
case 1 => if ((e2bits & 2) == 0) 2 else 3
case 2 => if ((e2bits & 4) == 0) 4 else 5
}
}
}
def shrinkFrom(p:JMicroShrinkRender, other:JMicroShrinkRender, renderBounds:Cuboid6):Int =
{
if(shrinkTest(p, other))
{
shrink(renderBounds, other.getBounds, shrinkSide(p.getSlot, other.getSlot))
}
else if(other.getSlot < 6 && !other.isTransparent)//other gets full face, we didn't shrink, flag rendermask
{
if(other.getSlot match {
case 0 => renderBounds.min.y <= 0
case 1 => renderBounds.max.y >= 1
case 2 => renderBounds.min.z <= 0
case 3 => renderBounds.max.z >= 1
case 4 => renderBounds.min.x <= 0
case 5 => renderBounds.max.x >= 1
})
return 1<<other.getSlot
}
return 0
}
def shrink(p:JMicroShrinkRender, renderBounds:Cuboid6, m:Int):Int =
{
var renderMask = 0
val part = p.asInstanceOf[TMultiPart]
val tile = part.tile
for(i <- 0 until m)
if(i != p.getSlot)
tile.partMap(i) match {
case other:JMicroShrinkRender =>
renderMask |= shrinkFrom(p, other, renderBounds)
case _ =>
}
return renderMask
}
def shrinkSide(s1:Int, s2:Int):Int = {
if(s2 < 6)//other is a cover
return s2
if(s1 < 15)//both corners
{
val c1 = s1-7
val c2 = s2-7
return c1^c2 match//different axis bits will become 1, same 0
{
case 1 => c2&1
case 2 => 2|(c2&2)>>1
case 4 => 4|(c2&4)>>2
case _ => -1
}
}
if(s2 < 15)//edge, other corner
{
val e1 = s1-15
val c2 = s2-7
val ebits = unpackEdgeBits(e1)
if((c2&edgeAxisMask(e1)) != ebits)
return -1
return (e1&0xC)>>1|(c2&(~ebits))>>(e1>>2)
}
//both edges
{
val e1 = s1-15
val e2 = s2-15
val e1bits = unpackEdgeBits(e1)
val e2bits = unpackEdgeBits(e2)
if((e1&0xC) == (e2&0xC))//same axis
{
return e1bits^e2bits match
{
case 1 => if((e2bits&1) == 0) 0 else 1
case 2 => if((e2bits&2) == 0) 2 else 3
case 4 => if((e2bits&4) == 0) 4 else 5
case _ => -1
}
}
else
{
val mask = edgeAxisMask(e1)&edgeAxisMask(e2)
if((e1bits&mask) != (e2bits&mask))
return -1
return e1>>2 match
{
case 0 => if((e2bits&1) == 0) 0 else 1
case 1 => if((e2bits&2) == 0) 2 else 3
case 2 => if((e2bits&4) == 0) 4 else 5
}
}
}
throw new IllegalArgumentException("Switch Falloff")
}
def recalcBounds(p:JMicroShrinkRender, renderBounds:Cuboid6) =
{
if(p.getSlot < 6)
shrink(p, renderBounds, 6)
else if(p.getSlot < 15)
shrink(p, renderBounds, 15)
else
shrink(p, renderBounds, 27)
}
def shapePriority(slot:Int):Int =
if(slot < 6)
return 2
else if(slot < 15)
return 1
else
return 0
def shrinkTest(a:JMicroShrinkRender, b:JMicroShrinkRender):Boolean =
{
if(a.getPriorityClass != b.getPriorityClass) return a.getPriorityClass < b.getPriorityClass
val shape1 = shapePriority(a.getSlot)
val shape2 = shapePriority(b.getSlot)
if(shape1 != shape2) return shape1 < shape2
if(a.getSlot < 6)//transparency takes precedence for covers
{
if(a.isTransparent != b.isTransparent) return a.isTransparent
if(a.getSize != b.getSize) return a.getSize < b.getSize
}
else
{
if(a.getSize != b.getSize) return a.getSize < b.getSize
if(a.isTransparent != b.isTransparent) return a.isTransparent
}
return a.getSlot < b.getSlot
throw new IllegalArgumentException("Switch Falloff")
}
def recalcBounds(p: JMicroShrinkRender, renderBounds: Cuboid6) = {
if (p.getSlot < 6)
shrink(p, renderBounds, 6)
else if (p.getSlot < 15)
shrink(p, renderBounds, 15)
else
shrink(p, renderBounds, 27)
}
def shapePriority(slot: Int): Int =
if (slot < 6)
return 2
else if (slot < 15)
return 1
else
return 0
def shrinkTest(a: JMicroShrinkRender, b: JMicroShrinkRender): Boolean = {
if (a.getPriorityClass != b.getPriorityClass)
return a.getPriorityClass < b.getPriorityClass
val shape1 = shapePriority(a.getSlot)
val shape2 = shapePriority(b.getSlot)
if (shape1 != shape2) return shape1 < shape2
if (a.getSlot < 6) // transparency takes precedence for covers
{
if (a.isTransparent != b.isTransparent) return a.isTransparent
if (a.getSize != b.getSize) return a.getSize < b.getSize
} else {
if (a.getSize != b.getSize) return a.getSize < b.getSize
if (a.isTransparent != b.isTransparent) return a.isTransparent
}
return a.getSlot < b.getSlot
}
}
trait JMicroShrinkRender
{
def getPriorityClass:Int
def getSlot:Int
def getSize:Int
def isTransparent:Boolean
def getBounds:Cuboid6
trait JMicroShrinkRender {
def getPriorityClass: Int
def getSlot: Int
def getSize: Int
def isTransparent: Boolean
def getBounds: Cuboid6
}
trait TMicroOcclusionClient extends TMicroOcclusion with JMicroShrinkRender
{
var renderBounds:Cuboid6 = _
var renderMask:Int = _
override def onPartChanged(part:TMultiPart)
{
super.onPartChanged(part)
recalcBounds()
}
override def onAdded()
{
super.onAdded()
recalcBounds()
}
trait TMicroOcclusionClient extends TMicroOcclusion with JMicroShrinkRender {
var renderBounds: Cuboid6 = _
var renderMask: Int = _
override def read(packet:MCDataInput)
{
super.read(packet)
recalcBounds()
}
def recalcBounds()
{
renderBounds = getBounds.copy
renderMask = MicroOcclusion.recalcBounds(this, renderBounds)
}
override def getPriorityClass = 0
override def onPartChanged(part: TMultiPart) {
super.onPartChanged(part)
recalcBounds()
}
override def onAdded() {
super.onAdded()
recalcBounds()
}
override def read(packet: MCDataInput) {
super.read(packet)
recalcBounds()
}
def recalcBounds() {
renderBounds = getBounds.copy
renderMask = MicroOcclusion.recalcBounds(this, renderBounds)
}
override def getPriorityClass = 0
}
trait TMicroOcclusion extends TMultiPart
{
import MicroOcclusion._
def getSlot:Int
def getSize:Int
def getMaterial:Int
def getBounds:Cuboid6
override def occlusionTest(npart:TMultiPart):Boolean =
{
if(!super.occlusionTest(npart))
trait TMicroOcclusion extends TMultiPart {
import MicroOcclusion._
def getSlot: Int
def getSize: Int
def getMaterial: Int
def getBounds: Cuboid6
override def occlusionTest(npart: TMultiPart): Boolean = {
if (!super.occlusionTest(npart))
return false
if (!npart.isInstanceOf[TMicroOcclusion])
return true
val mpart = npart.asInstanceOf[TMicroOcclusion]
val shape1 = shapePriority(getSlot)
val shape2 = shapePriority(mpart.getSlot)
if (mpart.getSize + getSize > 8) // intersecting if opposite
{
if (shape1 == 2 && shape2 == 2)
if (mpart.getSlot == (getSlot ^ 1))
return false
if(!npart.isInstanceOf[TMicroOcclusion])
return true
val mpart = npart.asInstanceOf[TMicroOcclusion]
val shape1 = shapePriority(getSlot)
val shape2 = shapePriority(mpart.getSlot)
if(mpart.getSize + getSize > 8)//intersecting if opposite
{
if(shape1 == 2 && shape2 == 2)
if(mpart.getSlot == (getSlot^1))
return false
if(mpart.getMaterial != getMaterial)
{
if(shape1 == 1 && shape2 == 1)
{
val axisMask = (getSlot-7)^(mpart.getSlot-7)
if(axisMask == 3 || axisMask == 5 || axisMask == 6)
return false
}
if(shape1 == 0 && shape2 == 1)
if(!edgeCornerOcclusionTest(this, mpart))
return false
if(shape1 == 1 && shape2 == 0)
if(!edgeCornerOcclusionTest(mpart, this))
return false
if(shape1 == 0 && shape2 == 0)
{
val e1 = getSlot-15
val e2 = mpart.getSlot-15
if((e1&0xC) == (e2&0xC) && ((e1&3)^(e2&3)) == 3)
return false
}
}
if (mpart.getMaterial != getMaterial) {
if (shape1 == 1 && shape2 == 1) {
val axisMask = (getSlot - 7) ^ (mpart.getSlot - 7)
if (axisMask == 3 || axisMask == 5 || axisMask == 6)
return false
}
if (shape1 == 0 && shape2 == 1)
if (!edgeCornerOcclusionTest(this, mpart))
return false
if (shape1 == 1 && shape2 == 0)
if (!edgeCornerOcclusionTest(mpart, this))
return false
if (shape1 == 0 && shape2 == 0) {
val e1 = getSlot - 15
val e2 = mpart.getSlot - 15
if ((e1 & 0xc) == (e2 & 0xc) && ((e1 & 3) ^ (e2 & 3)) == 3)
return false
}
}
return true
}
def edgeCornerOcclusionTest(edge:TMicroOcclusion, corner:TMicroOcclusion):Boolean =
{
((corner.getSlot-7)&edgeAxisMask(edge.getSlot-15)) == unpackEdgeBits(edge.getSlot-15)
}
}
}
return true
}
def edgeCornerOcclusionTest(
edge: TMicroOcclusion,
corner: TMicroOcclusion
): Boolean = {
((corner.getSlot - 7) & edgeAxisMask(edge.getSlot - 15)) == unpackEdgeBits(
edge.getSlot - 15
)
}
}
@@ -13,28 +13,32 @@ import cpw.mods.fml.common.Loader
import cpw.mods.fml.common.LoaderState
import net.minecraft.util.MovingObjectPosition.MovingObjectType
object MicroblockEventHandler
{
@SubscribeEvent
@SideOnly(Side.CLIENT)
def postTextureStitch(event:TextureStitchEvent.Post)
{
if(event.map.getTextureType == 0)
MicroMaterialRegistry.loadIcons()
}
object MicroblockEventHandler {
@SubscribeEvent
@SideOnly(Side.CLIENT)
def postTextureStitch(event: TextureStitchEvent.Post) {
if (event.map.getTextureType == 0)
MicroMaterialRegistry.loadIcons()
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event:DrawBlockHighlightEvent)
{
if(event.currentItem != null && event.currentItem.getItem == MicroblockProxy.itemMicro &&
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK)
{
GL11.glPushMatrix()
RenderUtils.translateToWorldCoords(event.player, event.partialTicks)
if(ItemMicroPartRenderer.renderHighlight(event.player, event.currentItem, event.target))
event.setCanceled(true)
GL11.glPopMatrix()
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event: DrawBlockHighlightEvent) {
if (
event.currentItem != null && event.currentItem.getItem == MicroblockProxy.itemMicro &&
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK
) {
GL11.glPushMatrix()
RenderUtils.translateToWorldCoords(event.player, event.partialTicks)
if (
ItemMicroPartRenderer.renderHighlight(
event.player,
event.currentItem,
event.target
)
)
event.setCanceled(true)
GL11.glPopMatrix()
}
}
}
}
@@ -12,35 +12,39 @@ import codechicken.microblock.ConfigContent
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent
import scala.collection.JavaConversions._
@Mod(modid = "ForgeMicroblock", acceptedMinecraftVersions = "[1.7.10]", dependencies = "required-after:ForgeMultipart", modLanguage = "scala")
object MicroblockMod
{
@EventHandler
def preInit(event: FMLPreInitializationEvent) {
MicroblockProxy.preInit(event.getModLog)
DefaultContent.load()
ConfigContent.parse(event.getModConfigurationDirectory)
}
@Mod(
modid = "ForgeMicroblock",
acceptedMinecraftVersions = "[1.7.10]",
dependencies = "required-after:ForgeMultipart",
modLanguage = "scala"
)
object MicroblockMod {
@EventHandler
def preInit(event: FMLPreInitializationEvent) {
MicroblockProxy.preInit(event.getModLog)
DefaultContent.load()
ConfigContent.parse(event.getModConfigurationDirectory)
}
@EventHandler
def init(event: FMLInitializationEvent) {
MicroblockProxy.init()
ConfigContent.load()
}
@EventHandler
def init(event: FMLInitializationEvent) {
MicroblockProxy.init()
ConfigContent.load()
}
@EventHandler
def postInit(event: FMLPostInitializationEvent) {
MicroMaterialRegistry.setupIDMap()
MicroblockProxy.postInit()
}
@EventHandler
def postInit(event: FMLPostInitializationEvent) {
MicroMaterialRegistry.setupIDMap()
MicroblockProxy.postInit()
}
@EventHandler
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MicroMaterialRegistry.setupIDMap()
}
@EventHandler
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MicroMaterialRegistry.setupIDMap()
}
@EventHandler
def handleIMC(event: IMCEvent) {
ConfigContent.handleIMC(event.getMessages)
}
@EventHandler
def handleIMC(event: IMCEvent) {
ConfigContent.handleIMC(event.getMessages)
}
}
@@ -1,6 +1,10 @@
package codechicken.microblock.handler
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
import codechicken.lib.packet.PacketCustom.{
IHandshakeHandler,
IClientPacketHandler,
IServerPacketHandler
}
import codechicken.lib.packet.PacketCustom
import net.minecraft.client.Minecraft
import codechicken.microblock.MicroMaterialRegistry
@@ -10,33 +14,52 @@ import net.minecraft.network.play.server.S40PacketDisconnect
import net.minecraft.util.ChatComponentTranslation
import net.minecraft.network.NetHandlerPlayServer
class MicroblockPH
{
val registryChannel = "ForgeMicroblock" //Must use the 250 system for ID registry as the NetworkMod idMap hasn't been properly initialized from the server yet.
class MicroblockPH {
val registryChannel =
"ForgeMicroblock" // Must use the 250 system for ID registry as the NetworkMod idMap hasn't been properly initialized from the server yet.
}
object MicroblockCPH extends MicroblockPH with IClientPacketHandler
{
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
packet.getType match {
case 1 => handleMaterialRegistration(packet, netHandler)
}
object MicroblockCPH extends MicroblockPH with IClientPacketHandler {
def handlePacket(
packet: PacketCustom,
mc: Minecraft,
netHandler: INetHandlerPlayClient
) {
packet.getType match {
case 1 => handleMaterialRegistration(packet, netHandler)
}
}
def handleMaterialRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
val missing = MicroMaterialRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("microblock.missing", missing.mkString(", "))))
}
def handleMaterialRegistration(
packet: PacketCustom,
netHandler: INetHandlerPlayClient
) {
val missing = MicroMaterialRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(
new S40PacketDisconnect(
new ChatComponentTranslation(
"microblock.missing",
missing.mkString(", ")
)
)
)
}
}
object MicroblockSPH extends MicroblockPH with IServerPacketHandler with IHandshakeHandler
{
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {}
object MicroblockSPH
extends MicroblockPH
with IServerPacketHandler
with IHandshakeHandler {
def handlePacket(
packet: PacketCustom,
sender: EntityPlayerMP,
netHandler: INetHandlerPlayServer
) {}
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MicroMaterialRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
}
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MicroMaterialRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
}
@@ -24,89 +24,112 @@ import org.apache.logging.log4j.Logger
import scala.collection.mutable
class MicroblockProxy_serverImpl
{
var logger: Logger = _
class MicroblockProxy_serverImpl {
var logger: Logger = _
var itemMicro: ItemMicroPart = _
var sawStone: Item = _
var sawIron: Item = _
var sawDiamond: Item = _
var stoneRod: Item = _
var itemMicro: ItemMicroPart = _
var sawStone: Item = _
var sawIron: Item = _
var sawDiamond: Item = _
var stoneRod: Item = _
var useSawIcons: Boolean = _
var useSawIcons: Boolean = _
def preInit(logger: Logger) {
this.logger = logger
itemMicro = new ItemMicroPart
GameRegistry.registerItem(itemMicro, "microblock")
sawStone = createSaw(config, "sawStone", 1)
sawIron = createSaw(config, "sawIron", 2)
sawDiamond = createSaw(config, "sawDiamond", 3)
stoneRod = new Item().setUnlocalizedName("microblock:stoneRod").setTextureName("microblock:stoneRod")
GameRegistry.registerItem(stoneRod, "stoneRod")
def preInit(logger: Logger) {
this.logger = logger
itemMicro = new ItemMicroPart
GameRegistry.registerItem(itemMicro, "microblock")
sawStone = createSaw(config, "sawStone", 1)
sawIron = createSaw(config, "sawIron", 2)
sawDiamond = createSaw(config, "sawDiamond", 3)
stoneRod = new Item()
.setUnlocalizedName("microblock:stoneRod")
.setTextureName("microblock:stoneRod")
GameRegistry.registerItem(stoneRod, "stoneRod")
OreDictionary.registerOre("rodStone", stoneRod)
MinecraftForge.EVENT_BUS.register(MicroblockEventHandler)
OreDictionary.registerOre("rodStone", stoneRod)
MinecraftForge.EVENT_BUS.register(MicroblockEventHandler)
useSawIcons = config.getTag("useSawIcons").setComment("Set to true to use mc style icons for the saw instead of the 3D model").getBooleanValue(false)
useSawIcons = config
.getTag("useSawIcons")
.setComment(
"Set to true to use mc style icons for the saw instead of the 3D model"
)
.getBooleanValue(false)
}
protected var saws = mutable.MutableList[Item]()
def createSaw(config: ConfigFile, name: String, strength: Int) = {
val saw = new ItemSaw(config.getTag(name).useBraces(), strength)
.setUnlocalizedName("microblock:" + name)
.setTextureName("microblock:" + name)
GameRegistry.registerItem(saw, name)
saws += saw
saw
}
def addSawRecipe(saw: Item, blade: Item) {
CraftingManager.getInstance.getRecipeList
.asInstanceOf[JList[IRecipe]]
.add(
new ShapedOreRecipe(
new ItemStack(saw),
"srr",
"sbr",
's': Character,
"stickWood",
'r': Character,
"rodStone",
'b': Character,
blade
)
)
}
def init() {
CraftingManager.getInstance.getRecipeList
.asInstanceOf[JList[IRecipe]]
.add(MicroRecipe)
if (!Loader.isModLoaded("dreamcraft")) {
CraftingManager.getInstance.addRecipe(
new ItemStack(stoneRod, 4),
"s",
"s",
's': Character,
Blocks.stone
)
addSawRecipe(sawStone, Items.flint)
addSawRecipe(sawIron, Items.iron_ingot)
addSawRecipe(sawDiamond, Items.diamond)
}
}
protected var saws = mutable.MutableList[Item]()
def createSaw(config: ConfigFile, name: String, strength: Int) = {
val saw = new ItemSaw(config.getTag(name).useBraces(), strength)
.setUnlocalizedName("microblock:" + name).setTextureName("microblock:" + name)
GameRegistry.registerItem(saw, name)
saws+=saw
saw
}
def addSawRecipe(saw: Item, blade: Item) {
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(
new ShapedOreRecipe(new ItemStack(saw),
"srr",
"sbr",
's': Character, "stickWood",
'r': Character, "rodStone",
'b': Character, blade))
}
def init() {
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(MicroRecipe)
if (!Loader.isModLoaded("dreamcraft")) {
CraftingManager.getInstance.addRecipe(new ItemStack(stoneRod, 4), "s", "s", 's': Character, Blocks.stone)
addSawRecipe(sawStone, Items.flint)
addSawRecipe(sawIron, Items.iron_ingot)
addSawRecipe(sawDiamond, Items.diamond)
}
}
def postInit() {
MicroMaterialRegistry.calcMaxCuttingStrength()
PacketCustom.assignHandshakeHandler(MicroblockSPH.registryChannel, MicroblockSPH)
}
def postInit() {
MicroMaterialRegistry.calcMaxCuttingStrength()
PacketCustom.assignHandshakeHandler(
MicroblockSPH.registryChannel,
MicroblockSPH
)
}
}
class MicroblockProxy_clientImpl extends MicroblockProxy_serverImpl
{
@SideOnly(Side.CLIENT)
lazy val renderBlocks = new RenderBlocks
class MicroblockProxy_clientImpl extends MicroblockProxy_serverImpl {
@SideOnly(Side.CLIENT)
lazy val renderBlocks = new RenderBlocks
@SideOnly(Side.CLIENT)
override def postInit() {
super.postInit()
MicroMaterialRegistry.loadIcons()
MinecraftForgeClient.registerItemRenderer(itemMicro, ItemMicroPartRenderer)
PacketCustom.assignHandler(MicroblockCPH.registryChannel, MicroblockCPH)
}
@SideOnly(Side.CLIENT)
override def postInit() {
super.postInit()
MicroMaterialRegistry.loadIcons()
MinecraftForgeClient.registerItemRenderer(itemMicro, ItemMicroPartRenderer)
PacketCustom.assignHandler(MicroblockCPH.registryChannel, MicroblockCPH)
}
@SideOnly(Side.CLIENT)
override def init() {
super.init()
saws.foreach(MinecraftForgeClient.registerItemRenderer(_, ItemSawRenderer))
}
@SideOnly(Side.CLIENT)
override def init() {
super.init()
saws.foreach(MinecraftForgeClient.registerItemRenderer(_, ItemSawRenderer))
}
}
object MicroblockProxy extends MicroblockProxy_clientImpl
{
}
object MicroblockProxy extends MicroblockProxy_clientImpl {}
@@ -2,7 +2,6 @@ package codechicken
import codechicken.microblock.handler.MicroblockProxy
package object microblock
{
def logger = MicroblockProxy.logger
package object microblock {
def logger = MicroblockProxy.logger
}
@@ -23,282 +23,429 @@ import net.minecraft.world.IBlockAccess
import codechicken.lib.raytracer.ExtendedMOP
import scala.collection.JavaConversions._
object BlockMultipart
{
def getTile(world:IBlockAccess, x:Int, y:Int, z:Int) = world.getTileEntity(x, y, z) match {
case t:TileMultipart if !t.partList.isEmpty => t
case _ => null
object BlockMultipart {
def getTile(world: IBlockAccess, x: Int, y: Int, z: Int) =
world.getTileEntity(x, y, z) match {
case t: TileMultipart if !t.partList.isEmpty => t
case _ => null
}
def getClientTile(world:IBlockAccess, x:Int, y:Int, z:Int) = world.getTileEntity(x, y, z) match {
case t:TileMultipartClient if !t.partList.isEmpty => t
case _ => null
def getClientTile(world: IBlockAccess, x: Int, y: Int, z: Int) =
world.getTileEntity(x, y, z) match {
case t: TileMultipartClient if !t.partList.isEmpty => t
case _ => null
}
def reduceMOP(hit:MovingObjectPosition):(Int, ExtendedMOP) = {
val ehit = hit.asInstanceOf[ExtendedMOP]
val data:(Int, _) = ExtendedMOP.getData(hit)
return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist))
}
def drawHighlight(world:World, player:EntityPlayer, hit:MovingObjectPosition, frame:Float):Boolean =
{
val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ)
if(tile == null)
return false
val (index, mop) = reduceMOP(hit)
if(tile.partList(index).drawHighlight(mop, player, frame))
return true
tile.partList(index).collisionRayTrace(RayTracer.getStartVec(player), RayTracer.getEndVec(player))
return false
}
def reduceMOP(hit: MovingObjectPosition): (Int, ExtendedMOP) = {
val ehit = hit.asInstanceOf[ExtendedMOP]
val data: (Int, _) = ExtendedMOP.getData(hit)
return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist))
}
def drawHighlight(
world: World,
player: EntityPlayer,
hit: MovingObjectPosition,
frame: Float
): Boolean = {
val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ)
if (tile == null)
return false
val (index, mop) = reduceMOP(hit)
if (tile.partList(index).drawHighlight(mop, player, frame))
return true
tile
.partList(index)
.collisionRayTrace(
RayTracer.getStartVec(player),
RayTracer.getEndVec(player)
)
return false
}
}
/**
* Block class for all multiparts, should be internal use only.
*/
class BlockMultipart extends Block(Material.rock)
{
import BlockMultipart._
override def hasTileEntity(meta:Int = 0) = true
override def isBlockSolid(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.isSolid(side)
}
override def isSideSolid(world:IBlockAccess, x:Int, y:Int, z:Int, side:ForgeDirection):Boolean =
isBlockSolid(world, x, y, z, side.ordinal())
/** Block class for all multiparts, should be internal use only.
*/
class BlockMultipart extends Block(Material.rock) {
import BlockMultipart._
override def canPlaceTorchOnTop(world:World, x:Int, y:Int, z:Int) =
getTile(world, x, y, z) match {
case null => false
case tile => tile.canPlaceTorchOnTop
}
override def hasTileEntity(meta: Int = 0) = true
override def onNeighborBlockChange(world:World, x:Int, y:Int, z:Int, block:Block) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onNeighborBlockChange()
override def isBlockSolid(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int
): Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.isSolid(side)
}
override def collisionRayTrace(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):ExtendedMOP =
getTile(world, x, y, z) match {
case null => null
case tile => tile.collisionRayTrace(start, end)
}
def rayTraceAll(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):Iterable[ExtendedMOP] =
getTile(world, x, y, z) match {
case null => Seq()
case tile => tile.rayTraceAll(start, end)
}
override def isSideSolid(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: ForgeDirection
): Boolean =
isBlockSolid(world, x, y, z, side.ordinal())
override def removedByPlayer(world:World, player:EntityPlayer, x:Int, y:Int, z:Int):Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if(hit == null || tile == null)
{
dropAndDestroy(world, x, y, z)
return true
}
val (index, mop) = reduceMOP(hit)
if(world.isRemote)
{
tile.partList(index).addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer)
return true
}
tile.harvestPart(index, mop, player)
return world.getTileEntity(x, y, z) == null
override def canPlaceTorchOnTop(world: World, x: Int, y: Int, z: Int) =
getTile(world, x, y, z) match {
case null => false
case tile => tile.canPlaceTorchOnTop
}
def dropAndDestroy(world:World, x:Int, y:Int, z:Int) {
val tile = getTile(world, x, y, z)
if(tile != null && !world.isRemote)
tile.dropItems(getDrops(world, x, y, z, 0, 0))
world.setBlockToAir(x, y, z)
}
override def quantityDropped(meta:Int, fortune:Int, random:Random) = 0
override def getDrops(world:World, x:Int, y:Int, z:Int, meta:Int, fortune:Int):ArrayList[ItemStack] = {
val ai = new ArrayList[ItemStack]()
if(world.isRemote)
return ai
val tile = getTile(world, x, y, z)
if(tile != null)
tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item)))
return ai
}
override def addCollisionBoxesToList(world:World, x:Int, y:Int, z:Int, ebb:AxisAlignedBB, list$:List[_], entity:Entity) {
val list = list$.asInstanceOf[List[AxisAlignedBB]]
val tile = getTile(world, x, y, z)
if(tile != null)
tile.partList.foreach(part =>
part.getCollisionBoxes.foreach{c =>
val aabb = c.toAABB.offset(x, y, z)
if(aabb.intersectsWith(ebb))
list.add(aabb)
})
}
override def addHitEffects(world:World, hit:MovingObjectPosition, effectRenderer:EffectRenderer):Boolean = {
val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ)
if(tile != null) {
val (index, mop) = reduceMOP(hit)
if(index < tile.partList.size)
tile.partList(index).addHitEffects(mop, effectRenderer)
}
return true
}
override def addDestroyEffects(world:World, x:Int, y:Int, z:Int, s:Int, effectRenderer:EffectRenderer) = true
override def renderAsNormalBlock() = false
override def isOpaqueCube = false
override def getRenderType = TileMultipart.renderID
override def isAir(world:IBlockAccess, x:Int, y:Int, z:Int):Boolean =
getTile(world, x, y, z) match {
case null => true
case tile => tile.partList.isEmpty
}
override def isReplaceable(world:IBlockAccess, x:Int, y:Int, z:Int) = isAir(world, x, y, z)
override def getRenderBlockPass = 1
override def canRenderInPass(pass:Int):Boolean = {
MultipartRenderer.pass = pass
return true
override def onNeighborBlockChange(
world: World,
x: Int,
y: Int,
z: Int,
block: Block
) {
val tile = getTile(world, x, y, z)
if (tile != null)
tile.onNeighborBlockChange()
}
override def collisionRayTrace(
world: World,
x: Int,
y: Int,
z: Int,
start: Vec3,
end: Vec3
): ExtendedMOP =
getTile(world, x, y, z) match {
case null => null
case tile => tile.collisionRayTrace(start, end)
}
override def getPickBlock(hit:MovingObjectPosition, world:World, x:Int, y:Int, z:Int):ItemStack = {
val tile = getTile(world, x, y, z)
if(tile != null)
{
if (!hit.isInstanceOf[ExtendedMOP]) {
return null
}
val (index, mop) = reduceMOP(hit)
return tile.partList(index).pickItem(mop)
def rayTraceAll(
world: World,
x: Int,
y: Int,
z: Int,
start: Vec3,
end: Vec3
): Iterable[ExtendedMOP] =
getTile(world, x, y, z) match {
case null => Seq()
case tile => tile.rayTraceAll(start, end)
}
override def removedByPlayer(
world: World,
player: EntityPlayer,
x: Int,
y: Int,
z: Int
): Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if (hit == null || tile == null) {
dropAndDestroy(world, x, y, z)
return true
}
val (index, mop) = reduceMOP(hit)
if (world.isRemote) {
tile
.partList(index)
.addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer)
return true
}
tile.harvestPart(index, mop, player)
return world.getTileEntity(x, y, z) == null
}
def dropAndDestroy(world: World, x: Int, y: Int, z: Int) {
val tile = getTile(world, x, y, z)
if (tile != null && !world.isRemote)
tile.dropItems(getDrops(world, x, y, z, 0, 0))
world.setBlockToAir(x, y, z)
}
override def quantityDropped(meta: Int, fortune: Int, random: Random) = 0
override def getDrops(
world: World,
x: Int,
y: Int,
z: Int,
meta: Int,
fortune: Int
): ArrayList[ItemStack] = {
val ai = new ArrayList[ItemStack]()
if (world.isRemote)
return ai
val tile = getTile(world, x, y, z)
if (tile != null)
tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item)))
return ai
}
override def addCollisionBoxesToList(
world: World,
x: Int,
y: Int,
z: Int,
ebb: AxisAlignedBB,
list$ : List[_],
entity: Entity
) {
val list = list$.asInstanceOf[List[AxisAlignedBB]]
val tile = getTile(world, x, y, z)
if (tile != null)
tile.partList.foreach(part =>
part.getCollisionBoxes.foreach { c =>
val aabb = c.toAABB.offset(x, y, z)
if (aabb.intersectsWith(ebb))
list.add(aabb)
}
)
}
override def addHitEffects(
world: World,
hit: MovingObjectPosition,
effectRenderer: EffectRenderer
): Boolean = {
val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ)
if (tile != null) {
val (index, mop) = reduceMOP(hit)
if (index < tile.partList.size)
tile.partList(index).addHitEffects(mop, effectRenderer)
}
return true
}
override def addDestroyEffects(
world: World,
x: Int,
y: Int,
z: Int,
s: Int,
effectRenderer: EffectRenderer
) = true
override def renderAsNormalBlock() = false
override def isOpaqueCube = false
override def getRenderType = TileMultipart.renderID
override def isAir(world: IBlockAccess, x: Int, y: Int, z: Int): Boolean =
getTile(world, x, y, z) match {
case null => true
case tile => tile.partList.isEmpty
}
override def isReplaceable(world: IBlockAccess, x: Int, y: Int, z: Int) =
isAir(world, x, y, z)
override def getRenderBlockPass = 1
override def canRenderInPass(pass: Int): Boolean = {
MultipartRenderer.pass = pass
return true
}
override def getPickBlock(
hit: MovingObjectPosition,
world: World,
x: Int,
y: Int,
z: Int
): ItemStack = {
val tile = getTile(world, x, y, z)
if (tile != null) {
if (!hit.isInstanceOf[ExtendedMOP]) {
return null
}
val (index, mop) = reduceMOP(hit)
return tile.partList(index).pickItem(mop)
}
return null
}
override def getPlayerRelativeBlockHardness(
player: EntityPlayer,
world: World,
x: Int,
y: Int,
z: Int
): Float = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if (hit != null && tile != null) {
val (index, mop) = reduceMOP(hit)
return tile.partList(index).getStrength(mop, player) / 30f
}
override def getPlayerRelativeBlockHardness(player:EntityPlayer, world:World, x:Int, y:Int, z:Int):Float = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if(hit != null && tile != null) {
val (index, mop) = reduceMOP(hit)
return tile.partList(index).getStrength(mop, player)/30F
}
return 1/100F
}
/**
* Kludge to set PROTECTED blockIcon to a blank icon
*/
override def registerBlockIcons(register:IIconRegister) {
val icon = TextureUtils.getBlankIcon(16, register)
setBlockTextureName(icon.getIconName)
super.registerBlockIcons(register)
}
override def getLightValue(world:IBlockAccess, x:Int, y:Int, z:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getLightValue
}
return 1 / 100f
}
override def randomDisplayTick(world:World, x:Int, y:Int, z:Int, random:Random) {
val tile = getClientTile(world, x, y, z)
if(tile != null)
tile.randomDisplayTick(random)
}
override def onBlockActivated(world:World, x:Int, y:Int, z:Int, player:EntityPlayer, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit == null)
return false
val tile = getTile(world, x, y, z)
if(tile == null)
return false
val (index, mop) = reduceMOP(hit)
return tile.partList(index).activate(player, mop, player.getHeldItem)
}
override def onBlockClicked(world:World, x:Int, y:Int, z:Int, player:EntityPlayer) {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit == null)
return
val tile = getTile(world, x, y, z)
if(tile == null)
return
val (index, mop) = reduceMOP(hit)
tile.partList(index).click(player, mop, player.getHeldItem)
}
override def isProvidingStrongPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.strongPowerLevel(side^1)
}
/** Kludge to set PROTECTED blockIcon to a blank icon
*/
override def registerBlockIcons(register: IIconRegister) {
val icon = TextureUtils.getBlankIcon(16, register)
setBlockTextureName(icon.getIconName)
super.registerBlockIcons(register)
}
override def isProvidingWeakPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.weakPowerLevel(side^1)
}
override def canConnectRedstone(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.canConnectRedstone(side)
}
override def onEntityCollidedWithBlock(world:World, x:Int, y:Int, z:Int, entity:Entity) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onEntityCollision(entity)
override def getLightValue(world: IBlockAccess, x: Int, y: Int, z: Int): Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getLightValue
}
override def onNeighborChange(world:IBlockAccess, x:Int, y:Int, z:Int, tileX:Int, tileY:Int, tileZ:Int) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onNeighborTileChange(tileX, tileY, tileZ)
override def randomDisplayTick(
world: World,
x: Int,
y: Int,
z: Int,
random: Random
) {
val tile = getClientTile(world, x, y, z)
if (tile != null)
tile.randomDisplayTick(random)
}
override def onBlockActivated(
world: World,
x: Int,
y: Int,
z: Int,
player: EntityPlayer,
side: Int,
hitX: Float,
hitY: Float,
hitZ: Float
): Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if (hit == null)
return false
val tile = getTile(world, x, y, z)
if (tile == null)
return false
val (index, mop) = reduceMOP(hit)
return tile.partList(index).activate(player, mop, player.getHeldItem)
}
override def onBlockClicked(
world: World,
x: Int,
y: Int,
z: Int,
player: EntityPlayer
) {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if (hit == null)
return
val tile = getTile(world, x, y, z)
if (tile == null)
return
val (index, mop) = reduceMOP(hit)
tile.partList(index).click(player, mop, player.getHeldItem)
}
override def isProvidingStrongPower(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int
): Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.strongPowerLevel(side ^ 1)
}
override def getExplosionResistance(entity:Entity, world:World, x:Int, y:Int, z:Int, explosionX:Double, explosionY:Double, explosionZ:Double):Float =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getExplosionResistance(entity)
}
override def isProvidingWeakPower(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int
): Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.weakPowerLevel(side ^ 1)
}
override def getWeakChanges(world:IBlockAccess, x:Int, y:Int, z:Int) =
getTile(world, x, y, z) match {
case null => false
case tile => tile.getWeakChanges
}
override def canConnectRedstone(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int
): Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.canConnectRedstone(side)
}
override def canProvidePower = true
override def onEntityCollidedWithBlock(
world: World,
x: Int,
y: Int,
z: Int,
entity: Entity
) {
val tile = getTile(world, x, y, z)
if (tile != null)
tile.onEntityCollision(entity)
}
override def onNeighborChange(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
tileX: Int,
tileY: Int,
tileZ: Int
) {
val tile = getTile(world, x, y, z)
if (tile != null)
tile.onNeighborTileChange(tileX, tileY, tileZ)
}
override def getExplosionResistance(
entity: Entity,
world: World,
x: Int,
y: Int,
z: Int,
explosionX: Double,
explosionY: Double,
explosionZ: Double
): Float =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getExplosionResistance(entity)
}
override def getWeakChanges(world: IBlockAccess, x: Int, y: Int, z: Int) =
getTile(world, x, y, z) match {
case null => false
case tile => tile.getWeakChanges
}
override def canProvidePower = true
}
@@ -11,47 +11,46 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.relauncher.{Side, SideOnly}
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
/**
* A class that maintains a map server<->client of which players are holding the control (or placement modifier key) much like sneaking.
*/
object ControlKeyModifer
{
implicit def playerControlValue(p:EntityPlayer) = new ControlKeyValue(p)
class ControlKeyValue(p:EntityPlayer)
{
def isControlDown = map(p)
}
val map = HashMap[EntityPlayer, Boolean]().withDefaultValue(false)
/**
* Implicit static for Java users.
*/
def isControlDown(p:EntityPlayer) = p.isControlDown
/** A class that maintains a map server<->client of which players are holding
* the control (or placement modifier key) much like sneaking.
*/
object ControlKeyModifer {
implicit def playerControlValue(p: EntityPlayer) = new ControlKeyValue(p)
class ControlKeyValue(p: EntityPlayer) {
def isControlDown = map(p)
}
val map = HashMap[EntityPlayer, Boolean]().withDefaultValue(false)
/** Implicit static for Java users.
*/
def isControlDown(p: EntityPlayer) = p.isControlDown
}
/**
* Key Handler implementation
*/
object ControlKeyHandler extends KeyBinding("key.control", Keyboard.KEY_LCONTROL, "key.categories.gameplay")
{
import ControlKeyModifer._
var wasPressed = false
/** Key Handler implementation
*/
object ControlKeyHandler
extends KeyBinding(
"key.control",
Keyboard.KEY_LCONTROL,
"key.categories.gameplay"
) {
import ControlKeyModifer._
var wasPressed = false
@SubscribeEvent
@SideOnly(Side.CLIENT)
def tick(event:ClientTickEvent) {
val pressed = getIsKeyPressed
if(pressed != wasPressed) {
wasPressed = pressed
if(Minecraft.getMinecraft.getNetHandler != null)
{
map.put(Minecraft.getMinecraft.thePlayer, pressed)
val packet = new PacketCustom(MultipartCPH.channel, 1)
packet.writeBoolean(pressed)
packet.sendToServer()
}
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def tick(event: ClientTickEvent) {
val pressed = getIsKeyPressed
if (pressed != wasPressed) {
wasPressed = pressed
if (Minecraft.getMinecraft.getNetHandler != null) {
map.put(Minecraft.getMinecraft.thePlayer, pressed)
val packet = new PacketCustom(MultipartCPH.channel, 1)
packet.writeBoolean(pressed)
packet.sendToServer()
}
}
}
}
}
@@ -3,31 +3,24 @@ package codechicken.multipart
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
/**
* Class for reading and writing ids, widening the carrier data type as necessary
*/
class IDWriter
{
var write:(MCDataOutput, Int)=>Unit = _
var read:(MCDataInput)=>Int = _
def setMax(i:Int)
{
val l = i.toLong & 0xFFFFFFFF
if(l > 0xFFFF)
{
write = (data, i) => data.writeInt(i)
read = (data) => data.readInt()
}
else if(l > 0xFF)
{
write = (data, i) => data.writeShort(i)
read = (data) => data.readUShort()
}
else
{
write = (data, i) => data.writeByte(i)
read = (data) => data.readUByte()
}
/** Class for reading and writing ids, widening the carrier data type as
* necessary
*/
class IDWriter {
var write: (MCDataOutput, Int) => Unit = _
var read: (MCDataInput) => Int = _
def setMax(i: Int) {
val l = i.toLong & 0xffffffff
if (l > 0xffff) {
write = (data, i) => data.writeInt(i)
read = (data) => data.readInt()
} else if (l > 0xff) {
write = (data, i) => data.writeShort(i)
read = (data) => data.readUShort()
} else {
write = (data, i) => data.writeByte(i)
read = (data) => data.readUByte()
}
}
}
}
@@ -1,15 +1,16 @@
package codechicken.multipart
/**
* Mixin interface for parts that want to be notified of neighbor tile change events (comparators or inventory maintainers)
*/
/** Mixin interface for parts that want to be notified of neighbor tile change
* events (comparators or inventory maintainers)
*/
trait INeighborTileChange {
/**
* Returns whether this part needs calls for tile changes through one solid block
*/
def weakTileChanges():Boolean
/**
* Callback for neighbor tile changes, from same function in Block
*/
def onNeighborTileChanged(side:Int, weak:Boolean)
}
/** Returns whether this part needs calls for tile changes through one solid
* block
*/
def weakTileChanges(): Boolean
/** Callback for neighbor tile changes, from same function in Block
*/
def onNeighborTileChanged(side: Int, weak: Boolean)
}
@@ -2,15 +2,13 @@ package codechicken.multipart
import java.util.Random
/**
* Interface for parts that need random display ticks (torches)
*
* Marker interface for TRandomDisplayTickTile
*/
trait IRandomDisplayTick
{
/**
* Called on a random display tick.
*/
def randomDisplayTick(random:Random)
}
/** Interface for parts that need random display ticks (torches)
*
* Marker interface for TRandomDisplayTickTile
*/
trait IRandomDisplayTick {
/** Called on a random display tick.
*/
def randomDisplayTick(random: Random)
}
@@ -7,237 +7,307 @@ import net.minecraft.util.Direction
import codechicken.lib.vec.Rotation._
import net.minecraft.init.Blocks
/**
* Interface for parts with redstone interaction
*
* Marker interface for TRedstoneTile. This means that if a part is an instance of IRedstonePart, the container tile may be cast to TRedstoneTile
*/
trait IRedstonePart
{
/**
* Returns the strong (indirect, through blocks) signal being emitted by this part to the specified side
*/
def strongPowerLevel(side:Int):Int
/**
* Returns the weak (direct) signal being emitted by this part to the specified side
*/
def weakPowerLevel(side:Int):Int
/**
* Returns true if this part can connect to redstone on the specified side. Blocking parts like covers will be handled by RedstoneInteractions
*/
def canConnectRedstone(side:Int):Boolean
/** Interface for parts with redstone interaction
*
* Marker interface for TRedstoneTile. This means that if a part is an instance
* of IRedstonePart, the container tile may be cast to TRedstoneTile
*/
trait IRedstonePart {
/** Returns the strong (indirect, through blocks) signal being emitted by this
* part to the specified side
*/
def strongPowerLevel(side: Int): Int
/** Returns the weak (direct) signal being emitted by this part to the
* specified side
*/
def weakPowerLevel(side: Int): Int
/** Returns true if this part can connect to redstone on the specified side.
* Blocking parts like covers will be handled by RedstoneInteractions
*/
def canConnectRedstone(side: Int): Boolean
}
/**
* For parts like wires that adhere to a specific face, reduces redstone connections to the specific edge between two faces.
* Should be implemented on parts implementing TFacePart
*/
trait IFaceRedstonePart extends IRedstonePart
{
/**
* Return the face to which this redstone part is attached
*/
def getFace:Int
/** For parts like wires that adhere to a specific face, reduces redstone
* connections to the specific edge between two faces. Should be implemented on
* parts implementing TFacePart
*/
trait IFaceRedstonePart extends IRedstonePart {
/** Return the face to which this redstone part is attached
*/
def getFace: Int
}
/**
* For parts that want to define their own connection masks (like center-center parts)
*/
trait IMaskedRedstonePart extends IRedstonePart
{
/**
* Returns the redstone connection mask for this part on side. See IRedstoneConnector for mask definition
*/
def getConnectionMask(side:Int):Int
/** For parts that want to define their own connection masks (like center-center
* parts)
*/
trait IMaskedRedstonePart extends IRedstonePart {
/** Returns the redstone connection mask for this part on side. See
* IRedstoneConnector for mask definition
*/
def getConnectionMask(side: Int): Int
}
/**
* Interface for tile entities which split their redstone connections into a mask for each side (edges and center)
*
* All connection masks are a 5 bit map.
* The lowest 4 bits correspond to the connection toward the face specified Rotation.rotateSide(side&6, b) where b is the bit index from lowest to highest.
* Bit 5 corresponds to a connection opposite side.
*/
trait IRedstoneConnector
{
/**
* Returns the redstone connection mask for this tile on side.
*/
def getConnectionMask(side:Int):Int
/**
* Returns the weak power level provided by this tile on side through mask
*/
def weakPowerLevel(side:Int, mask:Int):Int
/** Interface for tile entities which split their redstone connections into a
* mask for each side (edges and center)
*
* All connection masks are a 5 bit map. The lowest 4 bits correspond to the
* connection toward the face specified Rotation.rotateSide(side&6, b) where b
* is the bit index from lowest to highest. Bit 5 corresponds to a connection
* opposite side.
*/
trait IRedstoneConnector {
/** Returns the redstone connection mask for this tile on side.
*/
def getConnectionMask(side: Int): Int
/** Returns the weak power level provided by this tile on side through mask
*/
def weakPowerLevel(side: Int, mask: Int): Int
}
/**
* Internal interface for TileMultipart instances hosting IRedstonePart
*/
trait IRedstoneTile extends IRedstoneConnector
{
/**
* Returns a mask of spaces through which a wire could connect on side
*/
def openConnections(side:Int):Int
/** Internal interface for TileMultipart instances hosting IRedstonePart
*/
trait IRedstoneTile extends IRedstoneConnector {
/** Returns a mask of spaces through which a wire could connect on side
*/
def openConnections(side: Int): Int
}
/**
* Block version of IRedstoneConnector
* Due to the inadequate Block.canConnectRedstone not handling the bottom side (nor the top particularly well)
*/
trait IRedstoneConnectorBlock
{
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int
def weakPowerLevel(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, mask:Int):Int
/** Block version of IRedstoneConnector Due to the inadequate
* Block.canConnectRedstone not handling the bottom side (nor the top
* particularly well)
*/
trait IRedstoneConnectorBlock {
def getConnectionMask(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int
): Int
def weakPowerLevel(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int,
mask: Int
): Int
}
/**
* static helper class for calculating various things about redstone.
* Indirect power (also known as strong power) is not handled here, just use world.getIndirectPowerTo
* Masks are defined in IRedstoneConnector
*/
object RedstoneInteractions
{
import net.minecraft.util.Facing._
val vanillaSideMap = Array(-2, -1, 0, 2, 3, 1)
val sideVanillaMap = Array(1, 2, 5, 3, 4)
/** static helper class for calculating various things about redstone. Indirect
* power (also known as strong power) is not handled here, just use
* world.getIndirectPowerTo Masks are defined in IRedstoneConnector
*/
object RedstoneInteractions {
import net.minecraft.util.Facing._
/**
* Hardcoded vanilla overrides for Block.canConnectRedstone (see @IRedstoneConnectorBlock)
*/
val fullVanillaBlocks = Set(
Blocks.redstone_torch,
Blocks.unlit_redstone_torch,
Blocks.lever,
Blocks.stone_button,
Blocks.wooden_button,
Blocks.redstone_block)
/**
* Get the direct power to p on side
*/
def getPowerTo(p:TMultiPart, side:Int):Int =
{
val tile = p.tile
return getPowerTo(tile.getWorldObj, tile.xCoord, tile.yCoord, tile.zCoord, side,
tile.asInstanceOf[IRedstoneTile].openConnections(side)&connectionMask(p, side))
val vanillaSideMap = Array(-2, -1, 0, 2, 3, 1)
val sideVanillaMap = Array(1, 2, 5, 3, 4)
/** Hardcoded vanilla overrides for Block.canConnectRedstone (see
* \@IRedstoneConnectorBlock)
*/
val fullVanillaBlocks = Set(
Blocks.redstone_torch,
Blocks.unlit_redstone_torch,
Blocks.lever,
Blocks.stone_button,
Blocks.wooden_button,
Blocks.redstone_block
)
/** Get the direct power to p on side
*/
def getPowerTo(p: TMultiPart, side: Int): Int = {
val tile = p.tile
return getPowerTo(
tile.getWorldObj,
tile.xCoord,
tile.yCoord,
tile.zCoord,
side,
tile.asInstanceOf[IRedstoneTile].openConnections(side) & connectionMask(
p,
side
)
)
}
/** Get the direct power level to space (x, y, z) on side with mask
*/
def getPowerTo(
world: World,
x: Int,
y: Int,
z: Int,
side: Int,
mask: Int
): Int =
getPower(
world,
x + offsetsXForSide(side),
y + offsetsYForSide(side),
z + offsetsZForSide(side),
side ^ 1,
mask
)
/** Get the direct power level provided by space (x, y, z) on side with mask
*/
def getPower(
world: World,
x: Int,
y: Int,
z: Int,
side: Int,
mask: Int
): Int = {
val tile = world.getTileEntity(x, y, z)
if (tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].weakPowerLevel(side, mask)
val block = world.getBlock(x, y, z)
if (block.isInstanceOf[IRedstoneConnectorBlock])
return block
.asInstanceOf[IRedstoneConnectorBlock]
.weakPowerLevel(world, x, y, z, side, mask)
val vmask = vanillaConnectionMask(block, world, x, y, z, side, true)
if ((vmask & mask) > 0) {
var m = world.getIndirectPowerLevelTo(x, y, z, side ^ 1)
if (m < 15 && block == Blocks.redstone_wire)
m =
Math.max(m, world.getBlockMetadata(x, y, z)) // painful vanilla kludge
return m
}
/**
* Get the direct power level to space (x, y, z) on side with mask
*/
def getPowerTo(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
getPower(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, mask)
/**
* Get the direct power level provided by space (x, y, z) on side with mask
*/
def getPower(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].weakPowerLevel(side, mask)
val block = world.getBlock(x, y, z)
if(block.isInstanceOf[IRedstoneConnectorBlock])
return block.asInstanceOf[IRedstoneConnectorBlock].weakPowerLevel(world, x, y, z, side, mask)
val vmask = vanillaConnectionMask(block, world, x, y, z, side, true)
if((vmask&mask) > 0)
{
var m = world.getIndirectPowerLevelTo(x, y, z, side^1)
if(m < 15 && block == Blocks.redstone_wire)
m = Math.max(m, world.getBlockMetadata(x, y, z))//painful vanilla kludge
return m
}
return 0
}
def vanillaToSide(vside: Int) = sideVanillaMap(vside + 1)
/** Get the connection mask of the block on side of (x, y, z).
* @param power,
* whether the connection mask is for signal transfer or visual connection.
* (some blocks accept power without visual connection)
*/
def otherConnectionMask(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int,
power: Boolean
): Int =
getConnectionMask(
world,
x + offsetsXForSide(side),
y + offsetsYForSide(side),
z + offsetsZForSide(side),
side ^ 1,
power
)
/** Get the connection mask of part on side
*/
def connectionMask(p: TMultiPart, side: Int): Int = {
if (
p.isInstanceOf[IRedstonePart] && p
.asInstanceOf[IRedstonePart]
.canConnectRedstone(side)
) {
if (p.isInstanceOf[IFaceRedstonePart]) {
val fside = p.asInstanceOf[IFaceRedstonePart].getFace
if ((side & 6) == (fside & 6))
return 0x10
return 1 << rotationTo(side & 6, fside)
} else if (p.isInstanceOf[IMaskedRedstonePart]) {
return p.asInstanceOf[IMaskedRedstonePart].getConnectionMask(side)
}
return 0x1f
}
return 0
}
/** @param power
* If true, don't test canConnectRedstone on blocks, just get a power
* transmission mask rather than a visual connection
*/
def getConnectionMask(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int,
power: Boolean
): Int = {
val tile = world.getTileEntity(x, y, z)
if (tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].getConnectionMask(side)
val block = world.getBlock(x, y, z)
if (block.isInstanceOf[IRedstoneConnectorBlock])
return block
.asInstanceOf[IRedstoneConnectorBlock]
.getConnectionMask(world, x, y, z, side)
return vanillaConnectionMask(block, world, x, y, z, side, power)
}
/** Returns the connection mask for a vanilla block
*/
def vanillaConnectionMask(
block: Block,
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
side: Int,
power: Boolean
): Int = {
if (fullVanillaBlocks(block))
return 0x1f
if (side == 0) // vanilla doesn't handle side 0
return if (power) 0x1f else 0
/** so that these can be conducted to from face parts on the other side of
* the block. Due to vanilla's in adequecy with redstone/logic on walls
*/
if (
block == Blocks.redstone_wire || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator
) {
if (side != 0)
return if (power) 0x1f else 4
return 0
}
val vside = vanillaSideMap(side)
if (
block == Blocks.powered_repeater || block == Blocks.unpowered_repeater
) // stupid minecraft hardcodes
{
val meta = world.getBlockMetadata(x, y, z)
if (vside == (meta & 3) || vside == Direction.rotateOpposite(meta & 3))
return if (power) 0x1f else 4
return 0
}
def vanillaToSide(vside:Int) = sideVanillaMap(vside+1)
/**
* Get the connection mask of the block on side of (x, y, z).
* @param power, whether the connection mask is for signal transfer or visual connection. (some blocks accept power without visual connection)
*/
def otherConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
getConnectionMask(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, power)
/**
* Get the connection mask of part on side
*/
def connectionMask(p:TMultiPart, side:Int):Int =
{
if(p.isInstanceOf[IRedstonePart] && p.asInstanceOf[IRedstonePart].canConnectRedstone(side))
{
if(p.isInstanceOf[IFaceRedstonePart])
{
val fside = p.asInstanceOf[IFaceRedstonePart].getFace
if((side&6) == (fside&6))
return 0x10
return 1<<rotationTo(side&6, fside)
}
else if(p.isInstanceOf[IMaskedRedstonePart])
{
return p.asInstanceOf[IMaskedRedstonePart].getConnectionMask(side)
}
return 0x1F
}
return 0
}
/**
* @param power If true, don't test canConnectRedstone on blocks, just get a power transmission mask rather than a visual connection
*/
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].getConnectionMask(side)
val block = world.getBlock(x, y, z)
if(block.isInstanceOf[IRedstoneConnectorBlock])
return block.asInstanceOf[IRedstoneConnectorBlock].getConnectionMask(world, x, y, z, side)
return vanillaConnectionMask(block, world, x, y, z, side, power)
}
/**
* Returns the connection mask for a vanilla block
*/
def vanillaConnectionMask(block:Block, world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
{
if(fullVanillaBlocks(block))
return 0x1F
if(side == 0)//vanilla doesn't handle side 0
return if(power) 0x1F else 0
}
/**
* so that these can be conducted to from face parts on the other side of the block.
* Due to vanilla's in adequecy with redstone/logic on walls
*/
if(block == Blocks.redstone_wire || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator) {
if(side != 0)
return if(power) 0x1F else 4
if (
power || block.canConnectRedstone(world, x, y, z, vside)
) // some blocks accept power without visualising connections
return 0x1f
return 0
}
val vside = vanillaSideMap(side)
if(block == Blocks.powered_repeater || block == Blocks.unpowered_repeater)//stupid minecraft hardcodes
{
val meta = world.getBlockMetadata(x, y, z)
if(vside == (meta & 3) || vside == Direction.rotateOpposite(meta & 3))
return if(power) 0x1F else 4
return 0
}
if(power || block.canConnectRedstone(world, x, y, z, vside))//some blocks accept power without visualising connections
return 0x1F
return 0
}
}
return 0
}
}
@@ -8,47 +8,61 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.world.World
import codechicken.lib.vec.BlockCoord
/**
* Java class implementation
*/
/** Java class implementation
*/
abstract class JItemMultiPart extends Item with TItemMultiPart
/**
* Simple multipart item class for easy placement. Simply override the newPart function and it the part will be added to the block space if it passes the occlusion tests.
*/
trait TItemMultiPart extends Item
{
def getHitDepth(vhit:Vector3, side:Int):Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean =
{
val pos = new BlockCoord(x, y, z)
val vhit = new Vector3(hitX, hitY, hitZ)
val d = getHitDepth(vhit, side)
def place():Boolean =
{
val part = newPart(item, player, world, pos, side, vhit)
if(part == null || !TileMultipart.canPlacePart(world, pos, part))
return false
if(!world.isRemote)
TileMultipart.addPart(world, pos, part)
if(!player.capabilities.isCreativeMode)
item.stackSize-=1
return true
}
if(d < 1 && place())
return true
pos.offset(side)
return place()
/** Simple multipart item class for easy placement. Simply override the newPart
* function and it the part will be added to the block space if it passes the
* occlusion tests.
*/
trait TItemMultiPart extends Item {
def getHitDepth(vhit: Vector3, side: Int): Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side % 2 ^ 1)
override def onItemUse(
item: ItemStack,
player: EntityPlayer,
world: World,
x: Int,
y: Int,
z: Int,
side: Int,
hitX: Float,
hitY: Float,
hitZ: Float
): Boolean = {
val pos = new BlockCoord(x, y, z)
val vhit = new Vector3(hitX, hitY, hitZ)
val d = getHitDepth(vhit, side)
def place(): Boolean = {
val part = newPart(item, player, world, pos, side, vhit)
if (part == null || !TileMultipart.canPlacePart(world, pos, part))
return false
if (!world.isRemote)
TileMultipart.addPart(world, pos, part)
if (!player.capabilities.isCreativeMode)
item.stackSize -= 1
return true
}
/**
* Create a new part based on the placement information parameters.
*/
def newPart(item:ItemStack, player:EntityPlayer, world:World, pos:BlockCoord, side:Int, vhit:Vector3):TMultiPart
}
if (d < 1 && place())
return true
pos.offset(side)
return place()
}
/** Create a new part based on the placement information parameters.
*/
def newPart(
item: ItemStack,
player: EntityPlayer,
world: World,
pos: BlockCoord,
side: Int,
vhit: Vector3
): TMultiPart
}
@@ -15,223 +15,233 @@ import com.google.common.collect.ArrayListMultimap
import scala.collection.JavaConversions._
import net.minecraft.nbt.NBTTagCompound
/**
* This class handles the registration and internal ID mapping of all multipart classes.
*/
object MultiPartRegistry
{
/**
* Interface to be registered for constructing parts.
* Every instance of every multipart is constructed from an implementor of this.
*/
@Deprecated
trait IPartFactory
{
/**
* Create a new instance of the part with the specified type name identifier
* @param client If the part instance is for the client or the server
*/
def createPart(name: String, client: Boolean): TMultiPart
/** This class handles the registration and internal ID mapping of all multipart
* classes.
*/
object MultiPartRegistry {
/** Interface to be registered for constructing parts. Every instance of every
* multipart is constructed from an implementor of this.
*/
@Deprecated
trait IPartFactory {
/** Create a new instance of the part with the specified type name
* identifier
* @param client
* If the part instance is for the client or the server
*/
def createPart(name: String, client: Boolean): TMultiPart
}
/** Will replace IPartFactory in 1.8
*/
trait IPartFactory2 {
/** Create a new server instance of the part with the specified type name
* identifier
* @param nbt
* The tag compound that will be passed to part.load, can be used to
* change the class of part returned
*/
def createPart(name: String, nbt: NBTTagCompound): TMultiPart
/** Create a new client instance of the part with the specified type name
* identifier
* @param packet
* The packet that will be passed to part.readDesc, can be used to change
* the class of part returned
*/
def createPart(name: String, packet: MCDataInput): TMultiPart
}
/** An interface for converting existing blocks/tile entities to multipart
* versions.
*/
trait IPartConverter {
/** Return true if this converter can handle the specific blockID (may or
* may not actually convert the block)
*/
def blockTypes: Iterable[Block]
/** Return a multipart version of the block at pos in world. Return null if
* no conversion is possible.
*/
def convert(world: World, pos: BlockCoord): TMultiPart
}
private val typeMap = MMap[String, IPartFactory2]()
private val nameMap = MMap[String, Int]()
private var idMap: Array[(String, IPartFactory2)] = _
private val idWriter = new IDWriter
private val converters = ArrayListMultimap.create[Block, IPartConverter]()
private val containers = MMap[String, ModContainer]()
/** The state of the registry. 0 = no parts, 1 = registering, 2 = registered
*/
private var state: Int = 0
/** Register a part factory with an array of types it is capable of
* instantiating. Must be called before postInit
* @deprecated
* Use IPartFactory2
*/
@Deprecated
def registerParts(partFactory: IPartFactory, types: Array[String]) {
registerParts(partFactory.createPart _, types: _*)
}
/** Scala function version of registerParts
* @deprecated
* Use IPartFactory2
*/
@Deprecated
def registerParts(
partFactory: (String, Boolean) => TMultiPart,
types: String*
) {
registerParts(
new IPartFactory2 {
override def createPart(name: String, packet: MCDataInput) =
partFactory(name, true)
override def createPart(name: String, nbt: NBTTagCompound) =
partFactory(name, false)
},
types: _*
)
}
/** Register a part factory with an array of types it is capable of
* instantiating. Must be called before postInit
*/
def registerParts(partFactory: IPartFactory2, types: Array[String]) {
registerParts(partFactory, types: _*)
}
/** Scala va-args version of registerParts
*/
def registerParts(partFactory: IPartFactory2, types: String*) {
if (loaded)
throw new IllegalStateException(
"Parts must be registered in the init methods."
)
state = 1
val container = Loader.instance.activeModContainer
if (container == null)
throw new IllegalStateException(
"Parts must be registered during the initialization phase of a mod container"
)
types.foreach { s =>
if (typeMap.contains(s))
throw new IllegalStateException(
"Part with id " + s + " is already registered."
)
typeMap.put(s, partFactory)
containers.put(s, container)
}
}
/**
* Will replace IPartFactory in 1.8
*/
trait IPartFactory2
{
/**
* Create a new server instance of the part with the specified type name identifier
* @param nbt The tag compound that will be passed to part.load, can be used to change the class of part returned
*/
def createPart(name: String, nbt: NBTTagCompound): TMultiPart
/** Register a part converter instance
*/
def registerConverter(c: IPartConverter) {
c.blockTypes.foreach(converters.put(_, c))
}
/**
* Create a new client instance of the part with the specified type name identifier
* @param packet The packet that will be passed to part.readDesc, can be used to change the class of part returned
*/
def createPart(name: String, packet: MCDataInput): TMultiPart
private[multipart] def beforeServerStart() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[multipart] def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.length)
idMap.foreach(e => packet.writeString(e._1))
}
private[multipart] def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
/**
* An interface for converting existing blocks/tile entities to multipart versions.
*/
trait IPartConverter
{
/**
* Return true if this converter can handle the specific blockID (may or may not actually convert the block)
*/
def blockTypes: Iterable[Block]
/** Return true if any multiparts have been registered
*/
private[multipart] def required = state > 0
/**
* Return a multipart version of the block at pos in world. Return null if no conversion is possible.
*/
def convert(world: World, pos: BlockCoord): TMultiPart
/** Return true if no more parts can be registered
*/
def loaded = state == 2
private[multipart] def postInit() {
state = 2
}
/** Writes the id of part to data
*/
def writePartID(data: MCDataOutput, part: TMultiPart) {
idWriter.write(data, nameMap.get(part.getType).get)
}
/** Uses instantiators to creat a new part from the id read from data
*/
def readPart(data: MCDataInput) = {
val e = idMap(idWriter.read(data))
e._2.createPart(e._1, data)
}
/** Uses instantiators to creat a new part from the a tag compound
*/
def loadPart(name: String, nbt: NBTTagCompound) = typeMap.get(name) match {
case Some(factory) => factory.createPart(name, nbt)
case None =>
logger.error("Missing mapping for part with ID: " + name)
null
}
/** Uses instantiators to create a new part with specified identifier on side
* @deprecated
* currently calls the nbt/packet version with a null parameter, use
* readPart or loadPart instead
*/
@Deprecated
def createPart(name: String, client: Boolean) = typeMap.get(name) match {
case Some(factory) =>
if (client) factory.createPart(name, null: MCDataInput)
else factory.createPart(name, null: NBTTagCompound)
case None =>
logger.error("Missing mapping for part with ID: " + name)
null
}
/** Calls converters to create a multipart version of the block at pos
*/
def convertBlock(world: World, pos: BlockCoord, block: Block): TMultiPart = {
for (c <- converters.get(block)) {
val ret = c.convert(world, pos)
if (ret != null)
return ret
}
return null
}
private val typeMap = MMap[String, IPartFactory2]()
private val nameMap = MMap[String, Int]()
private var idMap: Array[(String, IPartFactory2)] = _
private val idWriter = new IDWriter
private val converters = ArrayListMultimap.create[Block, IPartConverter]()
private val containers = MMap[String, ModContainer]()
/**
* The state of the registry. 0 = no parts, 1 = registering, 2 = registered
*/
private var state: Int = 0
/**
* Register a part factory with an array of types it is capable of instantiating. Must be called before postInit
* @deprecated Use IPartFactory2
*/
@Deprecated
def registerParts(partFactory: IPartFactory, types: Array[String]) {
registerParts(partFactory.createPart _, types: _*)
}
/**
* Scala function version of registerParts
* @deprecated Use IPartFactory2
*/
@Deprecated
def registerParts(partFactory: (String, Boolean) => TMultiPart, types: String*) {
registerParts(new IPartFactory2 {
override def createPart(name: String, packet: MCDataInput) = partFactory(name, true)
override def createPart(name: String, nbt: NBTTagCompound) = partFactory(name, false)
}, types:_*)
}
/**
* Register a part factory with an array of types it is capable of instantiating. Must be called before postInit
*/
def registerParts(partFactory: IPartFactory2, types: Array[String]) {
registerParts(partFactory, types:_*)
}
/**
* Scala va-args version of registerParts
*/
def registerParts(partFactory: IPartFactory2, types: String*) {
if (loaded)
throw new IllegalStateException("Parts must be registered in the init methods.")
state = 1
val container = Loader.instance.activeModContainer
if (container == null)
throw new IllegalStateException("Parts must be registered during the initialization phase of a mod container")
types.foreach { s =>
if (typeMap.contains(s))
throw new IllegalStateException("Part with id " + s + " is already registered.")
typeMap.put(s, partFactory)
containers.put(s, container)
}
}
/**
* Register a part converter instance
*/
def registerConverter(c: IPartConverter) {
c.blockTypes.foreach(converters.put(_, c))
}
private[multipart] def beforeServerStart() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[multipart] def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.length)
idMap.foreach(e => packet.writeString(e._1))
}
private[multipart] def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
/**
* Return true if any multiparts have been registered
*/
private[multipart] def required = state > 0
/**
* Return true if no more parts can be registered
*/
def loaded = state == 2
private[multipart] def postInit() {
state = 2
}
/**
* Writes the id of part to data
*/
def writePartID(data: MCDataOutput, part: TMultiPart) {
idWriter.write(data, nameMap.get(part.getType).get)
}
/**
* Uses instantiators to creat a new part from the id read from data
*/
def readPart(data: MCDataInput) = {
val e = idMap(idWriter.read(data))
e._2.createPart(e._1, data)
}
/**
* Uses instantiators to creat a new part from the a tag compound
*/
def loadPart(name: String, nbt:NBTTagCompound) = typeMap.get(name) match {
case Some(factory) => factory.createPart(name, nbt)
case None =>
logger.error("Missing mapping for part with ID: " + name)
null
}
/**
* Uses instantiators to create a new part with specified identifier on side
* @deprecated currently calls the nbt/packet version with a null parameter, use readPart or loadPart instead
*/
@Deprecated
def createPart(name: String, client: Boolean) = typeMap.get(name) match {
case Some(factory) =>
if(client) factory.createPart(name, null: MCDataInput)
else factory.createPart(name, null: NBTTagCompound)
case None =>
logger.error("Missing mapping for part with ID: " + name)
null
}
/**
* Calls converters to create a multipart version of the block at pos
*/
def convertBlock(world: World, pos: BlockCoord, block: Block): TMultiPart = {
for (c <- converters.get(block)) {
val ret = c.convert(world, pos)
if (ret != null)
return ret
}
return null
}
def getModContainer(name: String) = containers(name)
def getModContainer(name: String) = containers(name)
}
@@ -11,176 +11,224 @@ import scala.collection.mutable.Map
import codechicken.multipart.asm.{ScratchBitSet, MultipartMixinFactory}
import codechicken.multipart.asm.ASMImplicits._
/**
* This class manages the dynamic construction and allocation of container TileMultipart instances.
*
* Classes that extend TileMultipart, adding tile centric logic, optimisations or interfaces, can be registered to a marker interface on a part instance.
* When a part is added to the tile that implements the certain marker interface, the container tile will be replaced with a class that includes the functionality from the corresponding mixin class.
*
* Classes are generated in a similar fashion to the way scala traits are compiled. To see the output, simply enable the config option and look in the asm/multipart folder of you .minecraft directory.
*
* There are several mixin traits that come with the API included in the scalatraits package. TPartialOcclusionTile is defined as class instead of trait to give an example for Java programmers.
*/
object MultipartGenerator extends ScratchBitSet
{
private val tileTraitMap = Map[Class[_], BitSet]()
private val interfaceTraitMap_c = Map[String, String]()
private val interfaceTraitMap_s = Map[String, String]()
private val partTraitMap_c = Map[Class[_], BitSet]()
private val partTraitMap_s = Map[Class[_], BitSet]()
private val clientTraitId = MultipartMixinFactory.registerTrait(classOf[TileMultipartClient])
private def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
private def interfaceTraitMap(client:Boolean) = if(client) interfaceTraitMap_c else interfaceTraitMap_s
/** This class manages the dynamic construction and allocation of container
* TileMultipart instances.
*
* Classes that extend TileMultipart, adding tile centric logic, optimisations
* or interfaces, can be registered to a marker interface on a part instance.
* When a part is added to the tile that implements the certain marker
* interface, the container tile will be replaced with a class that includes
* the functionality from the corresponding mixin class.
*
* Classes are generated in a similar fashion to the way scala traits are
* compiled. To see the output, simply enable the config option and look in the
* asm/multipart folder of you .minecraft directory.
*
* There are several mixin traits that come with the API included in the
* scalatraits package. TPartialOcclusionTile is defined as class instead of
* trait to give an example for Java programmers.
*/
object MultipartGenerator extends ScratchBitSet {
private val tileTraitMap = Map[Class[_], BitSet]()
private val interfaceTraitMap_c = Map[String, String]()
private val interfaceTraitMap_s = Map[String, String]()
private val partTraitMap_c = Map[Class[_], BitSet]()
private val partTraitMap_s = Map[Class[_], BitSet]()
private val clientTraitId =
MultipartMixinFactory.registerTrait(classOf[TileMultipartClient])
private def traitsForPart(part:TMultiPart, client:Boolean) =
partTraitMap(client).getOrElseUpdate(part.getClass, {
def heirachy(clazz:Class[_]):Seq[Class[_]] =
{
var superClasses:Seq[Class[_]] = clazz.getInterfaces.flatMap(c => heirachy(c)):+clazz
if(clazz.getSuperclass != null)
superClasses = superClasses++heirachy(clazz.getSuperclass)
return superClasses
}
private def partTraitMap(client: Boolean) =
if (client) partTraitMap_c else partTraitMap_s
private def interfaceTraitMap(client: Boolean) =
if (client) interfaceTraitMap_c else interfaceTraitMap_s
val map = interfaceTraitMap(client)
val traits = heirachy(part.getClass).flatMap(c => map.get(c.nodeName)).distinct
private def traitsForPart(part: TMultiPart, client: Boolean) =
partTraitMap(client).getOrElseUpdate(
part.getClass, {
def heirachy(clazz: Class[_]): Seq[Class[_]] = {
var superClasses: Seq[Class[_]] =
clazz.getInterfaces.flatMap(c => heirachy(c)) :+ clazz
if (clazz.getSuperclass != null)
superClasses = superClasses ++ heirachy(clazz.getSuperclass)
return superClasses
}
val bitset = new BitSet
traits.map(MultipartMixinFactory.getId).foreach(bitset.set)
bitset
})
val map = interfaceTraitMap(client)
val traits =
heirachy(part.getClass).flatMap(c => map.get(c.nodeName)).distinct
private def setTraits(part:TMultiPart, client:Boolean):BitSet = setTraits(Seq(part), client)
private def setTraits(parts:Iterable[TMultiPart], client:Boolean) = {
val bitset = freshBitSet
parts.foreach(p => bitset.or(traitsForPart(p, client)))
if(client) bitset.set(clientTraitId)
val bitset = new BitSet
traits.map(MultipartMixinFactory.getId).foreach(bitset.set)
bitset
}
}
)
/**
* Check if part adds any new interfaces to tile, if so, replace tile with a new copy and call tile.addPart(part)
* returns true if tile was replaced
*/
private[multipart] def addPart(world:World, pos:BlockCoord, part:TMultiPart):TileMultipart =
{
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
val bitset = setTraits(part, world.isRemote)
private def setTraits(part: TMultiPart, client: Boolean): BitSet =
setTraits(Seq(part), client)
var ntile = tile
if(ntile != null) {
if(converted) {//perform client conversion
ntile.partList(0).invalidateConvertedTile()
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
silentAddTile(world, pos, ntile)
PacketCustom.sendToChunk(new S23PacketBlockChange(pos.x, pos.y, pos.z, world), world, pos.x>>4, pos.z>>4)
ntile.partList(0).onConverted()
ntile.writeAddPart(ntile.partList(0))
}
private def setTraits(parts: Iterable[TMultiPart], client: Boolean) = {
val bitset = freshBitSet
parts.foreach(p => bitset.or(traitsForPart(p, client)))
if (client) bitset.set(clientTraitId)
bitset
}
val tileTraits = tileTraitMap(tile.getClass)
bitset.andNot(tileTraits)
if(!bitset.isEmpty) {
bitset.or(tileTraits)
ntile = MultipartMixinFactory.construct(bitset)
tile.setValid(false)
silentAddTile(world, pos, ntile)
ntile.from(tile)
}
}
else {
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
ntile = MultipartMixinFactory.construct(bitset)
silentAddTile(world, pos, ntile)
}
ntile.addPart_impl(part)
return ntile
}
/**
* Adds a tile entity to the world without notifying neighbor blocks or adding it to the tick list
*/
def silentAddTile(world:World, pos:BlockCoord, tile:TileEntity) {
val chunk = world.getChunkFromBlockCoords(pos.x, pos.z)
if(chunk != null)
chunk.func_150812_a(pos.x & 15, pos.y, pos.z & 15, tile)
}
/**
* Check if tile satisfies all the interfaces required by parts. If not, return a new generated copy of tile
*/
private[multipart] def generateCompositeTile(tile:TileEntity, parts:Iterable[TMultiPart], client:Boolean) = {
val bitset = setTraits(parts, client)
if(tile != null && tile.isInstanceOf[TileMultipart] && bitset == tileTraitMap(tile.getClass))
tile.asInstanceOf[TileMultipart]
else
MultipartMixinFactory.construct(bitset)
}
/**
* Check if there are any redundant interfaces on tile, if so, replace tile with new copy
*/
private[multipart] def partRemoved(tile:TileMultipart) = {
val ntile = generateCompositeTile(tile, tile.partList, tile.getWorldObj.isRemote)
if(ntile != tile) {
tile.setValid(false)
silentAddTile(tile.getWorldObj, new BlockCoord(tile), ntile)
ntile.from(tile)
ntile.notifyTileChange()
}
ntile
}
/**
* register s_trait to be applied to tiles containing parts implementing s_interface
*/
def registerTrait(s_interface:String, s_trait:String):Unit = registerTrait(s_interface, s_trait, s_trait)
/**
* register traits to be applied to tiles containing parts implementing s_interface
* c_trait for client worlds (may be null)
* s_trait for server worlds (may be null)
*/
def registerTrait(s_interface$:String, c_trait$:String, s_trait$:String) {
val s_interface = nodeName(s_interface$)
val c_trait = nodeName(c_trait$)
val s_trait = nodeName(s_trait$)
/** Check if part adds any new interfaces to tile, if so, replace tile with a
* new copy and call tile.addPart(part) returns true if tile was replaced
*/
private[multipart] def addPart(
world: World,
pos: BlockCoord,
part: TMultiPart
): TileMultipart = {
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
val bitset = setTraits(part, world.isRemote)
def registerSide(map:Map[String, String], traitName:String) = if(traitName != null) {
if(map.contains(s_interface))
logger.error("Trait already registered for "+s_interface)
else {
map.put(s_interface, traitName)
MultipartMixinFactory.registerTrait(traitName)
}
}
registerSide(interfaceTraitMap_c, c_trait)
registerSide(interfaceTraitMap_s, s_trait)
}
def registerPassThroughInterface(s_interface:String):Unit = registerPassThroughInterface(s_interface, true, true)
/**
* A passthrough interface, is an interface to be implemented on the container tile instance, for which all calls are passed directly to the single implementing part.
* Registering a passthrough interface is equivalent to defining a mixin class as follows.
* 1. field 'impl' which contains the reference to the corresponding part
* 2. occlusionTest is overriden to prevent more than one part with s_interface existing in the block space
* 3. implementing s_interface and passing all calls directly to the part instance.
*
* This allows compatibility with APIs that expect interfaces on the tile entity.
*/
def registerPassThroughInterface(s_interface:String, client:Boolean, server:Boolean) {
val tType = MultipartMixinFactory.generatePassThroughTrait(s_interface)
if(tType == null)
return
var ntile = tile
if (ntile != null) {
if (converted) { // perform client conversion
ntile.partList(0).invalidateConvertedTile()
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
silentAddTile(world, pos, ntile)
PacketCustom.sendToChunk(
new S23PacketBlockChange(pos.x, pos.y, pos.z, world),
world,
pos.x >> 4,
pos.z >> 4
)
ntile.partList(0).onConverted()
ntile.writeAddPart(ntile.partList(0))
}
registerTrait(s_interface, if(client) tType else null, if(server) tType else null)
val tileTraits = tileTraitMap(tile.getClass)
bitset.andNot(tileTraits)
if (!bitset.isEmpty) {
bitset.or(tileTraits)
ntile = MultipartMixinFactory.construct(bitset)
tile.setValid(false)
silentAddTile(world, pos, ntile)
ntile.from(tile)
}
} else {
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
ntile = MultipartMixinFactory.construct(bitset)
silentAddTile(world, pos, ntile)
}
private[multipart] def registerTileClass(clazz:Class[_ <: TileMultipart], traits:BitSet) {
tileTraitMap.put(clazz, traits.copy)
MultipartProxy.onTileClassBuilt(clazz)
ntile.addPart_impl(part)
return ntile
}
/** Adds a tile entity to the world without notifying neighbor blocks or
* adding it to the tick list
*/
def silentAddTile(world: World, pos: BlockCoord, tile: TileEntity) {
val chunk = world.getChunkFromBlockCoords(pos.x, pos.z)
if (chunk != null)
chunk.func_150812_a(pos.x & 15, pos.y, pos.z & 15, tile)
}
/** Check if tile satisfies all the interfaces required by parts. If not,
* return a new generated copy of tile
*/
private[multipart] def generateCompositeTile(
tile: TileEntity,
parts: Iterable[TMultiPart],
client: Boolean
) = {
val bitset = setTraits(parts, client)
if (
tile != null && tile
.isInstanceOf[TileMultipart] && bitset == tileTraitMap(tile.getClass)
)
tile.asInstanceOf[TileMultipart]
else
MultipartMixinFactory.construct(bitset)
}
/** Check if there are any redundant interfaces on tile, if so, replace tile
* with new copy
*/
private[multipart] def partRemoved(tile: TileMultipart) = {
val ntile =
generateCompositeTile(tile, tile.partList, tile.getWorldObj.isRemote)
if (ntile != tile) {
tile.setValid(false)
silentAddTile(tile.getWorldObj, new BlockCoord(tile), ntile)
ntile.from(tile)
ntile.notifyTileChange()
}
}
ntile
}
/** register s_trait to be applied to tiles containing parts implementing
* s_interface
*/
def registerTrait(s_interface: String, s_trait: String): Unit =
registerTrait(s_interface, s_trait, s_trait)
/** register traits to be applied to tiles containing parts implementing
* s_interface c_trait for client worlds (may be null) s_trait for server
* worlds (may be null)
*/
def registerTrait(
s_interface$ : String,
c_trait$ : String,
s_trait$ : String
) {
val s_interface = nodeName(s_interface$)
val c_trait = nodeName(c_trait$)
val s_trait = nodeName(s_trait$)
def registerSide(map: Map[String, String], traitName: String) = if (
traitName != null
) {
if (map.contains(s_interface))
logger.error("Trait already registered for " + s_interface)
else {
map.put(s_interface, traitName)
MultipartMixinFactory.registerTrait(traitName)
}
}
registerSide(interfaceTraitMap_c, c_trait)
registerSide(interfaceTraitMap_s, s_trait)
}
def registerPassThroughInterface(s_interface: String): Unit =
registerPassThroughInterface(s_interface, true, true)
/** A passthrough interface, is an interface to be implemented on the
* container tile instance, for which all calls are passed directly to the
* single implementing part. Registering a passthrough interface is
* equivalent to defining a mixin class as follows.
* 1. field 'impl' which contains the reference to the corresponding part
* 2. occlusionTest is overriden to prevent more than one part with
* s_interface existing in the block space 3. implementing s_interface
* and passing all calls directly to the part instance.
*
* This allows compatibility with APIs that expect interfaces on the tile
* entity.
*/
def registerPassThroughInterface(
s_interface: String,
client: Boolean,
server: Boolean
) {
val tType = MultipartMixinFactory.generatePassThroughTrait(s_interface)
if (tType == null)
return
registerTrait(
s_interface,
if (client) tType else null,
if (server) tType else null
)
}
private[multipart] def registerTileClass(
clazz: Class[_ <: TileMultipart],
traits: BitSet
) {
tileTraitMap.put(clazz, traits.copy)
MultipartProxy.onTileClassBuilt(clazz)
}
}
@@ -12,53 +12,56 @@ import net.minecraft.world.WorldServer
import java.util.Arrays
import java.lang.Iterable
/**
* Static helper class for handling the unusual way that multipart tile entities load from nbt and send description packets
* <br>
* Multipart tile entities will all save themselves with the id "savedMultipart" which if normally loaded by minecraft,
* will create a dummy tile entity which just holds the NBT it was read from. These dummies are then converted to actual container tiles on the ChunkLoad event.
* The createTileFromNBT function should be used to construct a multipart tile from NBT without the ChunkLoad event.
* <br>
* Multipart tile entities do not send description packets via the conventional means of one packet per tile when PlayerInstance calls for it, to do so would be terribly inefficient.
* Instead, the ChunkWatch event is used to batch all the describing data for a chunk into one packet which is compressed using relative positions.
* The sendDescPacket(s) functions should be used to send the description packet of a tile or tiles without a ChunkWatch event.
* <br>
* An example of using this class to move blocks/tile entites around can be found at www.chickenbones.craftsaddle.org/Files/Other/ItemDevTool2.java
*/
object MultipartHelper
{
/*val f_playersInChunk = classOf[PlayerInstance].getDeclaredField(
/** Static helper class for handling the unusual way that multipart tile
* entities load from nbt and send description packets <br> Multipart tile
* entities will all save themselves with the id "savedMultipart" which if
* normally loaded by minecraft, will create a dummy tile entity which just
* holds the NBT it was read from. These dummies are then converted to actual
* container tiles on the ChunkLoad event. The createTileFromNBT function
* should be used to construct a multipart tile from NBT without the ChunkLoad
* event. <br> Multipart tile entities do not send description packets via the
* conventional means of one packet per tile when PlayerInstance calls for it,
* to do so would be terribly inefficient. Instead, the ChunkWatch event is
* used to batch all the describing data for a chunk into one packet which is
* compressed using relative positions. The sendDescPacket(s) functions should
* be used to send the description packet of a tile or tiles without a
* ChunkWatch event. <br> An example of using this class to move blocks/tile
* entites around can be found at
* www.chickenbones.craftsaddle.org/Files/Other/ItemDevTool2.java
*/
object MultipartHelper {
/*val f_playersInChunk = classOf[PlayerInstance].getDeclaredField(
new ObfMapping("net/minecraft/server/management/PlayerInstance", "playersInChunk", "Ljava/util/List;")
.toRuntime.s_name)
f_playersInChunk.setAccessible(true)
def playersInChunk(inst:PlayerInstance) = f_playersInChunk.get(inst).asInstanceOf[List[_]]*/
def createTileFromNBT(world:World, tag:NBTTagCompound):TileEntity = {
if(!tag.getString("id").equals("savedMultipart"))
return null
MultipartSaveLoad.loadingWorld = world
return TileMultipart.createFromNBT(tag)
}
/**
* Note. This method should only be used to send tiles that have been created on the server mid-game via an NBT load to clients.
*/
def sendDescPacket(world:World, tile:TileEntity) {
val c = world.getChunkFromBlockCoords(tile.xCoord, tile.zCoord)
val pkt = MultipartSPH.getDescPacket(c, Arrays.asList(tile).iterator)
if(pkt != null)
pkt.sendToChunk(world, c.xPosition, c.zPosition)
}
/* Removed for compilation until PlayerInstance access transformer is pulled in forge
def createTileFromNBT(world: World, tag: NBTTagCompound): TileEntity = {
if (!tag.getString("id").equals("savedMultipart"))
return null
MultipartSaveLoad.loadingWorld = world
return TileMultipart.createFromNBT(tag)
}
/** Note. This method should only be used to send tiles that have been created
* on the server mid-game via an NBT load to clients.
*/
def sendDescPacket(world: World, tile: TileEntity) {
val c = world.getChunkFromBlockCoords(tile.xCoord, tile.zCoord)
val pkt = MultipartSPH.getDescPacket(c, Arrays.asList(tile).iterator)
if (pkt != null)
pkt.sendToChunk(world, c.xPosition, c.zPosition)
}
/* Removed for compilation until PlayerInstance access transformer is pulled in forge
def sendDescPackets(world:World, tiles:Iterable[TileEntity]) {
val map = LinkedListMultimap.create[Long, TileEntity]()
tiles.filter(_.isInstanceOf[TileMultipart]).foreach(t => map.put(t.xCoord.toLong<<32|t.zCoord, t))
val mgr = world.asInstanceOf[WorldServer].getPlayerManager
map.asMap.entrySet.foreach{e =>
map.asMap.entrySet.foreach{e =>
val coord = e.getKey
val c = world.getChunkFromBlockCoords((coord>>32).toInt, coord.toInt)
lazy val pkt = MultipartSPH.getDescPacket(c, e.getValue.iterator)
@@ -68,40 +71,42 @@ object MultipartHelper
}
}*/
/**
* Helper class for converting tile entities to multiparts on chunk load
*/
abstract class IPartTileConverter[T <: TileEntity](val clazz:Class[T])
{
/**
* @return true if this tile can be converted by this converter. If canConvert returns true, but convertMulti returns no parts, the tile will be deleted
*/
def canConvert(tile:TileEntity) = clazz.isInstance(tile)
/** Helper class for converting tile entities to multiparts on chunk load
*/
abstract class IPartTileConverter[T <: TileEntity](val clazz: Class[T]) {
def convert(tile:TileEntity) = convertMulti(tile.asInstanceOf[T])
/** @return
* true if this tile can be converted by this converter. If canConvert
* returns true, but convertMulti returns no parts, the tile will be
* deleted
*/
def canConvert(tile: TileEntity) = clazz.isInstance(tile)
/**
* Convert the TileEntity into multiple parts
* @return An iterable list of parts or an empty list to delete the tile
*/
def convertMulti(tile:T):Iterable[TMultiPart] = convertOne(tile) match {
case null => Seq()
case p => Seq(p)
}
/**
* Convert the TileEntity into a part
* @return The converted part, or null to delete the tile
*/
def convertOne(tile:T):TMultiPart
def convert(tile: TileEntity) = convertMulti(tile.asInstanceOf[T])
/** Convert the TileEntity into multiple parts
* @return
* An iterable list of parts or an empty list to delete the tile
*/
def convertMulti(tile: T): Iterable[TMultiPart] = convertOne(tile) match {
case null => Seq()
case p => Seq(p)
}
def registerTileConverter(converter:IPartTileConverter[_]) {
MultipartSaveLoad.converters += converter
}
/** Convert the TileEntity into a part
* @return
* The converted part, or null to delete the tile
*/
def convertOne(tile: T): TMultiPart
}
def createTileFromParts(parts:Iterable[TMultiPart]) = {
val tile = MultipartGenerator.generateCompositeTile(null, parts, false)
tile.loadParts(parts)
tile
}
}
def registerTileConverter(converter: IPartTileConverter[_]) {
MultipartSaveLoad.converters += converter
}
def createTileFromParts(parts: Iterable[TMultiPart]) = {
val tile = MultipartGenerator.generateCompositeTile(null, parts, false)
tile.loadParts(parts)
tile
}
}
@@ -15,60 +15,83 @@ import cpw.mods.fml.relauncher.Side
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.lighting.LightMatrix
/**
* Internal class for rendering callbacks. Should be moved to the handler package
*/
/** Internal class for rendering callbacks. Should be moved to the handler
* package
*/
@SideOnly(Side.CLIENT)
object MultipartRenderer extends TileEntitySpecialRenderer with ISimpleBlockRenderingHandler
{
TileMultipart.renderID = RenderingRegistry.getNextAvailableRenderId
var pass:Int = 0
override def renderTileEntityAt(t:TileEntity, x:Double, y:Double, z:Double, f:Float)
{
val tmpart = t.asInstanceOf[TileMultipartClient]
if(tmpart.partList.isEmpty)
return
object MultipartRenderer
extends TileEntitySpecialRenderer
with ISimpleBlockRenderingHandler {
TileMultipart.renderID = RenderingRegistry.getNextAvailableRenderId
var pass: Int = 0
CCRenderState.reset()
CCRenderState.pullLightmap()
CCRenderState.useNormals = true
val pos = new Vector3(x, y, z)
tmpart.renderDynamic(pos, f, pass)
override def renderTileEntityAt(
t: TileEntity,
x: Double,
y: Double,
z: Double,
f: Float
) {
val tmpart = t.asInstanceOf[TileMultipartClient]
if (tmpart.partList.isEmpty)
return
CCRenderState.reset()
CCRenderState.pullLightmap()
CCRenderState.useNormals = true
val pos = new Vector3(x, y, z)
tmpart.renderDynamic(pos, f, pass)
}
override def getRenderId = TileMultipart.renderID
override def renderWorldBlock(
world: IBlockAccess,
x: Int,
y: Int,
z: Int,
block: Block,
modelId: Int,
renderer: RenderBlocks
): Boolean = {
val t = world.getTileEntity(x, y, z)
if (!t.isInstanceOf[TileMultipartClient])
return false
val tmpart = t.asInstanceOf[TileMultipartClient]
if (tmpart.partList.isEmpty)
return false
if (renderer.hasOverrideBlockTexture) {
val hit = Minecraft.getMinecraft.objectMouseOver
if (
hit != null && hit.blockX == x && hit.blockY == y && hit.blockZ == z && ExtendedMOP
.getData(hit)
.isInstanceOf[(_, _)]
) {
val hitInfo: (Int, _) = ExtendedMOP.getData(hit)
if (
hitInfo._1.isInstanceOf[
Int
] && hitInfo._1 >= 0 && hitInfo._1 < tmpart.partList.size
)
tmpart.partList(hitInfo._1).drawBreaking(renderer)
}
return false
}
override def getRenderId = TileMultipart.renderID
override def renderWorldBlock(world:IBlockAccess, x:Int, y:Int, z:Int, block:Block, modelId:Int, renderer:RenderBlocks):Boolean =
{
val t = world.getTileEntity(x, y, z)
if(!t.isInstanceOf[TileMultipartClient])
return false
val tmpart = t.asInstanceOf[TileMultipartClient]
if(tmpart.partList.isEmpty)
return false
if(renderer.hasOverrideBlockTexture)
{
val hit = Minecraft.getMinecraft.objectMouseOver
if(hit != null && hit.blockX == x && hit.blockY == y && hit.blockZ == z && ExtendedMOP.getData(hit).isInstanceOf[(_, _)])
{
val hitInfo:(Int, _) = ExtendedMOP.getData(hit)
if(hitInfo._1.isInstanceOf[Int] && hitInfo._1 >= 0 && hitInfo._1 < tmpart.partList.size)
tmpart.partList(hitInfo._1).drawBreaking(renderer)
}
return false
}
CCRenderState.reset()
CCRenderState.lightMatrix.locate(world, x, y, z)
return tmpart.renderStatic(new Vector3(x, y, z), pass)
}
override def renderInventoryBlock(block:Block, meta:Int, modelId:Int, renderer:RenderBlocks) {}
CCRenderState.reset()
CCRenderState.lightMatrix.locate(world, x, y, z)
return tmpart.renderStatic(new Vector3(x, y, z), pass)
}
override def renderInventoryBlock(
block: Block,
meta: Int,
modelId: Int,
renderer: RenderBlocks
) {}
def shouldRender3DInInventory(modelId:Int) = false
def shouldRender3DInInventory(modelId: Int) = false
}
@@ -4,79 +4,77 @@ import scala.collection.mutable.{Map => MMap}
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
/**
* Static class for packing update data.
* When a specific property of a part changes and needs sending to the client, a bit can be set in the mask.
* This bit can then be checked in the writeScheduled callback.
* This prevents sending multiple packets if the same property updates more than once per tick.
*/
object PacketScheduler
{
private val map = MMap[TMultiPart, Long]()
/**
* Add bits to the current update mask for part. (binary OR)
*/
def schedulePacket(part:TMultiPart, mask:Long) {
if(part.world.isRemote)
throw new IllegalArgumentException("Cannot use PacketScheduler on a client world")
/** Static class for packing update data. When a specific property of a part
* changes and needs sending to the client, a bit can be set in the mask. This
* bit can then be checked in the writeScheduled callback. This prevents
* sending multiple packets if the same property updates more than once per
* tick.
*/
object PacketScheduler {
private val map = MMap[TMultiPart, Long]()
map.put(part, map.getOrElse(part, 0L)|mask)
}
/** Add bits to the current update mask for part. (binary OR)
*/
def schedulePacket(part: TMultiPart, mask: Long) {
if (part.world.isRemote)
throw new IllegalArgumentException(
"Cannot use PacketScheduler on a client world"
)
private[multipart] def sendScheduled() {
map.foreach{ e =>
val (part, mask) = e
if(part.tile != null) {
val ipart = part.asInstanceOf[IScheduledPacketPart]
val w = part.getWriteStream
ipart.maskWidth match {
case 1 => w.writeByte(mask.toInt)
case 2 => w.writeShort(mask.toInt)
case 4 => w.writeInt(mask.toInt)
case 8 => w.writeLong(mask)
}
ipart.writeScheduled(mask, w)
}
map.put(part, map.getOrElse(part, 0L) | mask)
}
private[multipart] def sendScheduled() {
map.foreach { e =>
val (part, mask) = e
if (part.tile != null) {
val ipart = part.asInstanceOf[IScheduledPacketPart]
val w = part.getWriteStream
ipart.maskWidth match {
case 1 => w.writeByte(mask.toInt)
case 2 => w.writeShort(mask.toInt)
case 4 => w.writeInt(mask.toInt)
case 8 => w.writeLong(mask)
}
map.clear()
ipart.writeScheduled(mask, w)
}
}
map.clear()
}
}
/**
* Callback interface for PacketScheduler
*/
trait IScheduledPacketPart
{
/**
* Write scheduled data to the packet, mask is the cumulative mask from calls to schedulePacket
*/
def writeScheduled(mask:Long, packet:MCDataOutput)
/**
* Returns the width (in bytes) of the data type required to hold all valid mask bits. Valid values are 1, 2, 4 and 8
*/
def maskWidth:Int
/**
* Read data matching mask. Estiablishes a method for subclasses to override. This should be called from read
*/
def readScheduled(mask:Long, packet:MCDataInput)
/** Callback interface for PacketScheduler
*/
trait IScheduledPacketPart {
/** Write scheduled data to the packet, mask is the cumulative mask from calls
* to schedulePacket
*/
def writeScheduled(mask: Long, packet: MCDataOutput)
/** Returns the width (in bytes) of the data type required to hold all valid
* mask bits. Valid values are 1, 2, 4 and 8
*/
def maskWidth: Int
/** Read data matching mask. Estiablishes a method for subclasses to override.
* This should be called from read
*/
def readScheduled(mask: Long, packet: MCDataInput)
}
trait TScheduledPacketPart extends TMultiPart with IScheduledPacketPart
{
final override def read(packet:MCDataInput) {
val mask = maskWidth match {
case 1 => packet.readUByte
case 2 => packet.readUShort
case 4 => packet.readInt
case 8 => packet.readLong
}
readScheduled(mask, packet)
trait TScheduledPacketPart extends TMultiPart with IScheduledPacketPart {
final override def read(packet: MCDataInput) {
val mask = maskWidth match {
case 1 => packet.readUByte
case 2 => packet.readUShort
case 4 => packet.readInt
case 8 => packet.readLong
}
def writeScheduled(mask:Long, packet:MCDataOutput){}
def readScheduled(mask:Long, packet:MCDataInput){}
}
readScheduled(mask, packet)
}
def writeScheduled(mask: Long, packet: MCDataOutput) {}
def readScheduled(mask: Long, packet: MCDataInput) {}
}
@@ -1,10 +1,11 @@
package codechicken.multipart;
/**
* Defines what each slot in a multipart tile corresponds to and provides some utility functions.
* For performance reasons, it is recommended that integer constants be useds as opposed to this enum
* Defines what each slot in a multipart tile corresponds to and provides some utility functions. For performance
* reasons, it is recommended that integer constants be useds as opposed to this enum
*/
public enum PartMap {
BOTTOM(0),
TOP(1),
NORTH(2),
@@ -63,9 +64,9 @@ public enum PartMap {
}
/**
* Returns a 3 bit mask of the axis xzy that are variable in this edge.
* For example, the first 4 edges (15-18) are along the Y axis, variable in x and z, so the mask is 110b. Note axis y is 1<<0, z is 1<<1 and x is 1<<2
* Note the parameter e is relative to the first edge slot and can range from 0-11
* Returns a 3 bit mask of the axis xzy that are variable in this edge. For example, the first 4 edges (15-18) are
* along the Y axis, variable in x and z, so the mask is 110b. Note axis y is 1<<0, z is 1<<1 and x is 1<<2 Note the
* parameter e is relative to the first edge slot and can range from 0-11
*/
public static int edgeAxisMask(int e) {
switch (e >> 2) {
@@ -80,9 +81,9 @@ public enum PartMap {
}
/**
* Unpacks an edge index, to a mask where high values indicate positive positions in that axis.
* Note the parameter e is relative to the first edge slot and can range from 0-11
* For example, edge 1 (slot 16), is EDGE_NYP so the mask returned would be 010 as z is positive.
* Unpacks an edge index, to a mask where high values indicate positive positions in that axis. Note the parameter e
* is relative to the first edge slot and can range from 0-11 For example, edge 1 (slot 16), is EDGE_NYP so the mask
* returned would be 010 as z is positive.
*/
public static int unpackEdgeBits(int e) {
switch (e >> 2) {
@@ -97,8 +98,8 @@ public enum PartMap {
}
/**
* Repacks a mask of axis bits indicating positive positions, into an edge in along the same axis as e.
* Note the parameter e is relative to the first edge slot and can range from 0-11
* Repacks a mask of axis bits indicating positive positions, into an edge in along the same axis as e. Note the
* parameter e is relative to the first edge slot and can range from 0-11
*/
public static int packEdgeBits(int e, int bits) {
switch (e >> 2) {
@@ -112,12 +113,8 @@ public enum PartMap {
throw new IllegalArgumentException("Switch Falloff");
}
private static int[] edgeBetweenMap = new int[] {
-1, -1, 8, 10, 4, 5,
-1, -1, 9, 11, 6, 7,
-1, -1, -1, -1, 0, 2,
-1, -1, -1, -1, 1, 3
};
private static int[] edgeBetweenMap = new int[] { -1, -1, 8, 10, 4, 5, -1, -1, 9, 11, 6, 7, -1, -1, -1, -1, 0, 2,
-1, -1, -1, -1, 1, 3 };
/**
* Returns the slot of the edge between 2 sides.
@@ -10,30 +10,32 @@ import codechicken.lib.vec.Translation
import codechicken.lib.render.uv.IconTransformation
import cpw.mods.fml.relauncher.{Side, SideOnly}
/**
* Java class implementation
*/
/** Java class implementation
*/
abstract class JCuboidPart extends TCuboidPart
/**
* Trait for parts that are simply a cuboid, having one bounding box. Overrides multipart functions to this effect.
*/
trait TCuboidPart extends TMultiPart
{
/**
* Return the bounding Cuboid6 for this part.
*/
def getBounds:Cuboid6
override def getSubParts:Iterable[IndexedCuboid6] = Seq(new IndexedCuboid6(0, getBounds))
override def getCollisionBoxes:Iterable[Cuboid6] = Seq(getBounds)
/** Trait for parts that are simply a cuboid, having one bounding box. Overrides
* multipart functions to this effect.
*/
trait TCuboidPart extends TMultiPart {
@SideOnly(Side.CLIENT)
override def drawBreaking(renderBlocks:RenderBlocks)
{
CCRenderState.reset()
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
BlockRenderer.renderCuboid(getBounds, 0)
}
}
/** Return the bounding Cuboid6 for this part.
*/
def getBounds: Cuboid6
override def getSubParts: Iterable[IndexedCuboid6] = Seq(
new IndexedCuboid6(0, getBounds)
)
override def getCollisionBoxes: Iterable[Cuboid6] = Seq(getBounds)
@SideOnly(Side.CLIENT)
override def drawBreaking(renderBlocks: RenderBlocks) {
CCRenderState.reset()
CCRenderState.setPipeline(
new Translation(x, y, z),
new IconTransformation(renderBlocks.overrideBlockTexture)
)
BlockRenderer.renderCuboid(getBounds, 0)
}
}
@@ -1,12 +1,11 @@
package codechicken.multipart
/**
* Interface which must be implemented by parts that go in an edge slot.
*/
trait TEdgePart extends TSlottedPart
{
/**
* Return true if this part can conduct redstone signal or let redstone signal pass through it.
*/
def conductsRedstone = false
}
/** Interface which must be implemented by parts that go in an edge slot.
*/
trait TEdgePart extends TSlottedPart {
/** Return true if this part can conduct redstone signal or let redstone
* signal pass through it.
*/
def conductsRedstone = false
}
@@ -1,16 +1,17 @@
package codechicken.multipart
/**
* Inteface which must be implemented by parts that go in a face part.
*/
trait TFacePart extends TSlottedPart
{
/**
* Passed down from Block.isSideSolid. Return true if this part is solid and opaque on the specified side
*/
def solid(side:Int):Boolean = true
/**
* Return the redstone conduction map for which signal can pass through this part on the face. Eg, hollow covers return 0x10 as signal can pass through the center hole.
*/
def redstoneConductionMap = 0
}
/** Inteface which must be implemented by parts that go in a face part.
*/
trait TFacePart extends TSlottedPart {
/** Passed down from Block.isSideSolid. Return true if this part is solid and
* opaque on the specified side
*/
def solid(side: Int): Boolean = true
/** Return the redstone conduction map for which signal can pass through this
* part on the face. Eg, hollow covers return 0x10 as signal can pass through
* the center hole.
*/
def redstoneConductionMap = 0
}
@@ -10,72 +10,83 @@ import codechicken.lib.vec.Vector3
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.render.EntityDigIconFX
/**
* This suite of 3 classes provides simple functions for standard minecraft style hit and break particles.
*
* Scala|Java composition setup.
* Due to the lack of mixin inheritance in Java, the classes are structured to suit both languages as follows.
* IconHitEffects contains static implementations of the functions that would be overriden in TMultiPart
* JIconHitEffects is the interface that should be implemented by a Java class,
* which can then override the functions in TMultipart and call the static methods in IconHitEffects with 'this' as the first parameter
* TIconHitEffects is a trait for scala implementors that does includes the overrides/static calls that Java programmers need to include themselves.
*/
object IconHitEffects
{
def addHitEffects(part:JIconHitEffects, hit:MovingObjectPosition, effectRenderer:EffectRenderer)
{
EntityDigIconFX.addBlockHitEffects(part.tile.getWorldObj,
part.getBounds.copy.add(Vector3.fromTileEntity(part.tile)), hit.sideHit,
part.getBreakingIcon(ExtendedMOP.getData(hit), hit.sideHit), effectRenderer)
}
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer)
{
addDestroyEffects(part, effectRenderer, true)
}
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer, scaleDensity:Boolean)
{
val icons = new Array[IIcon](6)
for(i <- 0 until 6)
icons(i) = part.getBrokenIcon(i)
val bounds =
if(scaleDensity) part.getBounds.copy
else Cuboid6.full.copy
EntityDigIconFX.addBlockDestroyEffects(part.tile.getWorldObj,
bounds.add(Vector3.fromTileEntity(part.tile)), icons, effectRenderer)
}
/** This suite of 3 classes provides simple functions for standard minecraft
* style hit and break particles.
*
* Scala|Java composition setup. Due to the lack of mixin inheritance in Java,
* the classes are structured to suit both languages as follows. IconHitEffects
* contains static implementations of the functions that would be overriden in
* TMultiPart JIconHitEffects is the interface that should be implemented by a
* Java class, which can then override the functions in TMultipart and call the
* static methods in IconHitEffects with 'this' as the first parameter
* TIconHitEffects is a trait for scala implementors that does includes the
* overrides/static calls that Java programmers need to include themselves.
*/
object IconHitEffects {
def addHitEffects(
part: JIconHitEffects,
hit: MovingObjectPosition,
effectRenderer: EffectRenderer
) {
EntityDigIconFX.addBlockHitEffects(
part.tile.getWorldObj,
part.getBounds.copy.add(Vector3.fromTileEntity(part.tile)),
hit.sideHit,
part.getBreakingIcon(ExtendedMOP.getData(hit), hit.sideHit),
effectRenderer
)
}
def addDestroyEffects(part: JIconHitEffects, effectRenderer: EffectRenderer) {
addDestroyEffects(part, effectRenderer, true)
}
def addDestroyEffects(
part: JIconHitEffects,
effectRenderer: EffectRenderer,
scaleDensity: Boolean
) {
val icons = new Array[IIcon](6)
for (i <- 0 until 6)
icons(i) = part.getBrokenIcon(i)
val bounds =
if (scaleDensity) part.getBounds.copy
else Cuboid6.full.copy
EntityDigIconFX.addBlockDestroyEffects(
part.tile.getWorldObj,
bounds.add(Vector3.fromTileEntity(part.tile)),
icons,
effectRenderer
)
}
}
/**
* Java interface containing callbacks for particle rendering.
* Make sure to override addHitEffects and addDestroyEffects as in TIconHitEffects
*/
trait JIconHitEffects extends TMultiPart
{
def getBounds:Cuboid6
@SideOnly(Side.CLIENT)
def getBreakingIcon(subPart:Any, side:Int):IIcon = getBrokenIcon(side)
@SideOnly(Side.CLIENT)
def getBrokenIcon(side:Int):IIcon
/** Java interface containing callbacks for particle rendering. Make sure to
* override addHitEffects and addDestroyEffects as in TIconHitEffects
*/
trait JIconHitEffects extends TMultiPart {
def getBounds: Cuboid6
@SideOnly(Side.CLIENT)
def getBreakingIcon(subPart: Any, side: Int): IIcon = getBrokenIcon(side)
@SideOnly(Side.CLIENT)
def getBrokenIcon(side: Int): IIcon
}
/**
* Trait for scala programmers
*/
trait TIconHitEffects extends JIconHitEffects
{
@SideOnly(Side.CLIENT)
override def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer)
{
IconHitEffects.addHitEffects(this, hit, effectRenderer)
}
@SideOnly(Side.CLIENT)
override def addDestroyEffects(effectRenderer:EffectRenderer)
{
IconHitEffects.addDestroyEffects(this, effectRenderer)
}
}
/** Trait for scala programmers
*/
trait TIconHitEffects extends JIconHitEffects {
@SideOnly(Side.CLIENT)
override def addHitEffects(
hit: MovingObjectPosition,
effectRenderer: EffectRenderer
) {
IconHitEffects.addHitEffects(this, hit, effectRenderer)
}
@SideOnly(Side.CLIENT)
override def addDestroyEffects(effectRenderer: EffectRenderer) {
IconHitEffects.addDestroyEffects(this, effectRenderer)
}
}
@@ -23,282 +23,338 @@ import net.minecraft.util.Vec3
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.raytracer.RayTracer
abstract class TMultiPart
{
/**
* Reference to the container TileMultipart instance
*/
var tile:TileMultipart = _
/**
* Legacy helper function for getting the tile entity (from when TileMultipart was a trait). Use tile() now.
*/
@Deprecated
def getTile:TileEntity = tile
/**
* Getter for tile.worldObj
*/
def world = if(tile == null) null else tile.getWorldObj
/**
* Short getter for xCoord
*/
def x = tile.xCoord
/**
* Short getter for yCoord
*/
def y = tile.yCoord
/**
* Short getter for zCoord
*/
def z = tile.zCoord
/**
* The unique string identifier for this class of multipart.
*/
def getType:String
/**
* Called when the container tile instance is changed to update reference
*/
def bind(t:TileMultipart)
{
tile = t
}
/**
* Perform an occlusion test to determine whether this and npart can 'fit' in this block space.
*/
def occlusionTest(npart:TMultiPart):Boolean = true
/**
* Return a list of entity collision boxes.
* Note all Cuboid6's returned by methods in TMultiPart should be within (0,0,0)->(1,1,1)
*/
def getCollisionBoxes:Iterable[Cuboid6] = Seq()
/**
* Perform a raytrace of this part. The default implementation does a Cuboid6 ray trace on bounding boxes returned from getSubParts.
* This should only be overridden if you need special ray-tracing capabilities such as triangular faces.
* The returned ExtendedMOP will be passed to methods such as 'activate' so it is recommended to use the data field to indicate information about the hit area.
*/
def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
val offset = new Vector3(x, y, z)
val boxes = getSubParts.map(c => new IndexedCuboid6(c.data, c.copy.add(offset)))
return RayTracer.instance.rayTraceCuboids(new Vector3(start), new Vector3(end), boxes.toList,
new BlockCoord(x, y, z), tile.getBlockType).asInstanceOf[ExtendedMOP]
}
/**
* For the default collisionRayTrace implementation, returns a list of indexed bounding boxes. The data field of ExtendedMOP will be set to the index of the cuboid the raytrace hit.
*/
def getSubParts:Iterable[IndexedCuboid6] = Seq()
/**
* Return a list of items that should be dropped when this part is destroyed.
*/
def getDrops:Iterable[ItemStack] = Seq()
/**
* Return a value indicating how hard this part is to break
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def getStrength(hit:MovingObjectPosition, player:EntityPlayer):Float = 1
/**
* Harvest this part, removing it from the container tile and dropping items if necessary.
* @param hit An instance of ExtendedMOP from collisionRayTrace
* @param player The player harvesting the part
*/
def harvest(hit:MovingObjectPosition, player:EntityPlayer)
{
if(!player.capabilities.isCreativeMode)
tile.dropItems(getDrops)
tile.remPart(this)
}
/**
* The light level emitted by this part
*/
def getLightValue = 0
abstract class TMultiPart {
/**
* If any part returns true for this, torches can be placed. Vanilla hacks...
*/
def canPlaceTorchOnTop = false
/** Reference to the container TileMultipart instance
*/
var tile: TileMultipart = _
/**
* Explosion resistance of the host tile is the maximum explosion resistance of the contained parts
* @param entity The entity responsible for this explosion
* @return The resistance of this part the the explosion
*/
def explosionResistance(entity:Entity) = 0F
/** Legacy helper function for getting the tile entity (from when
* TileMultipart was a trait). Use tile() now.
*/
@Deprecated
def getTile: TileEntity = tile
/**
* Add particles and other effects when a player is mining this part
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer){}
/**
* Add particles and other effects when a player broke this part
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addDestroyEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer) {addDestroyEffects(effectRenderer)}
@SideOnly(Side.CLIENT)
@Deprecated
def addDestroyEffects(effectRenderer:EffectRenderer){}
/**
* Render the static, unmoving faces of this part into the world renderer.
* The Tessellator is already drawing.
* @param pass The render pass, 1 or 0
* @return true if vertices were added to the tessellator
*/
@SideOnly(Side.CLIENT)
def renderStatic(pos:Vector3, pass:Int) = false
/**
* Render the dynamic, changing faces of this part and other gfx as in a TESR.
* The Tessellator will need to be started if it is to be used.
* @param pos The position of this block space relative to the renderer, same as x, y, z passed to TESR.
* @param frame The partial interpolation frame value for animations between ticks
* @param pass The render pass, 1 or 0
*/
@SideOnly(Side.CLIENT)
def renderDynamic(pos:Vector3, frame:Float, pass:Int){}
/**
* Draw the breaking overlay for this part. The overrideIcon in RenderBlocks will be set to the fracture icon.
*/
@SideOnly(Side.CLIENT)
def drawBreaking(renderBlocks:RenderBlocks){}
/**
* Override the drawing of the selection box around this part.
* @param hit An instance of ExtendedMOP from collisionRayTrace
* @return true if highlight rendering was overridden.
*/
@SideOnly(Side.CLIENT)
def drawHighlight(hit:MovingObjectPosition, player:EntityPlayer, frame:Float):Boolean = false
/**
* @return A Cuboid6 bounding the render of this part for frustum culling. The bounds are relative to the tile coordinates.
*/
def getRenderBounds = Cuboid6.full
/** Getter for tile.worldObj
*/
def world = if (tile == null) null else tile.getWorldObj
/**
* Write all the data required to describe a client version of this part to the packet.
* Called serverside, when a client loads this part for the first time.
*/
def writeDesc(packet:MCDataOutput){}
/**
* Fill out this part with the description information contained in packet. Will be exactly as written from writeDesc.
* Called clientside when a client loads this part for the first time.
*/
def readDesc(packet:MCDataInput){}
/**
* Save part to NBT (only called serverside)
*/
def save(tag:NBTTagCompound){}
/**
* Load part from NBT (only called serverside)
*/
def load(tag:NBTTagCompound){}
/**
* Gets a MCDataOutput instance for writing update data to clients with this part loaded.
* The write stream functions as a buffer which is flushed in a compressed databurst packet at the end of the tick.
*/
def getWriteStream:MCDataOutput = tile.getWriteStream(this)
/**
* Read and operate on data written to getWriteStream. Ensure all data this part wrote is read even if it's not going to be used.
* The default implementation assumes a call to sendDescUpdate as the only use of getWriteStream.
*/
def read(packet:MCDataInput)
{
readDesc(packet)
tile.markRender()
}
/**
* Quick and easy method to re-describe the whole part on the client. This will call read on the client which calls readDesc unless overriden.
* Incremental changes should be sent rather than the whole description packet if possible.
*/
def sendDescUpdate() = writeDesc(getWriteStream)
/**
* Called when a part is added or removed from this block space.
* The part parameter may be null if several things have changed.
*/
def onPartChanged(part:TMultiPart){}
/**
* Called when a neighbor block changed
*/
def onNeighborChanged(){}
/**
* Called when this part is added to the block space
*/
def onAdded() = onWorldJoin()
/**
* Called when this part is removed from the block space
*/
def onRemoved() = onWorldSeparate()
/**
* Called when the containing chunk is loaded on the server.
*/
def onChunkLoad() = onWorldJoin()
/**
* Called when the containing chunk is unloaded on the server.
*/
def onChunkUnload() = onWorldSeparate()
/**
* Called when this part separates from the world (due to removal, chunk unload or other). Use this to sync with external data structures. Called on both client and server
*/
def onWorldSeparate(){}
/**
* Called when this part joins the world (due to placement, chunkload or frame move etc). Use this to sync with external data structures. Called on both client and server
*/
def onWorldJoin(){}
/**
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered)
*/
def onConverted() = onAdded()
/**
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered) before the original tile has been replaced
* Use this to clear out things like inventory from the old tile.
*/
def invalidateConvertedTile(){}
/**
* Called when this part has been moved without a save/load.
*/
def onMoved() = onWorldJoin()
/**
* Called just before this part is actually removed from the container tile
*/
def preRemove(){}
/**
* Return whether this part needs update ticks. This will only be called on addition/removal so it should be a constant for this instance.
*/
def doesTick = true
/**
* Called once per world tick. This will be called even if doesTick returns false if another part in the space needs ticks.
*/
def update(){}
/**
* Called when a scheduled tick is executed.
*/
def scheduledTick(){}
/**
* Sets a scheduledTick callback for this part ticks in the future. This is a world time value, so if the chunk is unloaded and reloaded some time later, the tick may fire immediately.
*/
def scheduleTick(ticks:Int) = TickScheduler.scheduleTick(this, ticks)
/**
* Return the itemstack for the middle click pick-block function.
*/
def pickItem(hit:MovingObjectPosition):ItemStack = null
/**
* Called on block right click. item is the player's held item.
* This should not modify the part client side. If the client call returns false, the server will not call this function.
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def activate(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack) = false
/**
* Called on block left click. item is the player's held item.
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def click(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack){}
/**
* Called when an entity is within this block space. May not actually collide with this part.
*/
def onEntityCollision(entity:Entity){}
}
/** Short getter for xCoord
*/
def x = tile.xCoord
/** Short getter for yCoord
*/
def y = tile.yCoord
/** Short getter for zCoord
*/
def z = tile.zCoord
/** The unique string identifier for this class of multipart.
*/
def getType: String
/** Called when the container tile instance is changed to update reference
*/
def bind(t: TileMultipart) {
tile = t
}
/** Perform an occlusion test to determine whether this and npart can 'fit' in
* this block space.
*/
def occlusionTest(npart: TMultiPart): Boolean = true
/** Return a list of entity collision boxes. Note all Cuboid6's returned by
* methods in TMultiPart should be within (0,0,0)->(1,1,1)
*/
def getCollisionBoxes: Iterable[Cuboid6] = Seq()
/** Perform a raytrace of this part. The default implementation does a Cuboid6
* ray trace on bounding boxes returned from getSubParts. This should only be
* overridden if you need special ray-tracing capabilities such as triangular
* faces. The returned ExtendedMOP will be passed to methods such as
* 'activate' so it is recommended to use the data field to indicate
* information about the hit area.
*/
def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
val offset = new Vector3(x, y, z)
val boxes =
getSubParts.map(c => new IndexedCuboid6(c.data, c.copy.add(offset)))
return RayTracer.instance
.rayTraceCuboids(
new Vector3(start),
new Vector3(end),
boxes.toList,
new BlockCoord(x, y, z),
tile.getBlockType
)
.asInstanceOf[ExtendedMOP]
}
/** For the default collisionRayTrace implementation, returns a list of
* indexed bounding boxes. The data field of ExtendedMOP will be set to the
* index of the cuboid the raytrace hit.
*/
def getSubParts: Iterable[IndexedCuboid6] = Seq()
/** Return a list of items that should be dropped when this part is destroyed.
*/
def getDrops: Iterable[ItemStack] = Seq()
/** Return a value indicating how hard this part is to break
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
*/
def getStrength(hit: MovingObjectPosition, player: EntityPlayer): Float = 1
/** Harvest this part, removing it from the container tile and dropping items
* if necessary.
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
* @param player
* The player harvesting the part
*/
def harvest(hit: MovingObjectPosition, player: EntityPlayer) {
if (!player.capabilities.isCreativeMode)
tile.dropItems(getDrops)
tile.remPart(this)
}
/** The light level emitted by this part
*/
def getLightValue = 0
/** If any part returns true for this, torches can be placed. Vanilla hacks...
*/
def canPlaceTorchOnTop = false
/** Explosion resistance of the host tile is the maximum explosion resistance
* of the contained parts
* @param entity
* The entity responsible for this explosion
* @return
* The resistance of this part the the explosion
*/
def explosionResistance(entity: Entity) = 0f
/** Add particles and other effects when a player is mining this part
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addHitEffects(
hit: MovingObjectPosition,
effectRenderer: EffectRenderer
) {}
/** Add particles and other effects when a player broke this part
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addDestroyEffects(
hit: MovingObjectPosition,
effectRenderer: EffectRenderer
) { addDestroyEffects(effectRenderer) }
@SideOnly(Side.CLIENT)
@Deprecated
def addDestroyEffects(effectRenderer: EffectRenderer) {}
/** Render the static, unmoving faces of this part into the world renderer.
* The Tessellator is already drawing.
* @param pass
* The render pass, 1 or 0
* @return
* true if vertices were added to the tessellator
*/
@SideOnly(Side.CLIENT)
def renderStatic(pos: Vector3, pass: Int) = false
/** Render the dynamic, changing faces of this part and other gfx as in a
* TESR. The Tessellator will need to be started if it is to be used.
* @param pos
* The position of this block space relative to the renderer, same as x, y,
* z passed to TESR.
* @param frame
* The partial interpolation frame value for animations between ticks
* @param pass
* The render pass, 1 or 0
*/
@SideOnly(Side.CLIENT)
def renderDynamic(pos: Vector3, frame: Float, pass: Int) {}
/** Draw the breaking overlay for this part. The overrideIcon in RenderBlocks
* will be set to the fracture icon.
*/
@SideOnly(Side.CLIENT)
def drawBreaking(renderBlocks: RenderBlocks) {}
/** Override the drawing of the selection box around this part.
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
* @return
* true if highlight rendering was overridden.
*/
@SideOnly(Side.CLIENT)
def drawHighlight(
hit: MovingObjectPosition,
player: EntityPlayer,
frame: Float
): Boolean = false
/** @return
* A Cuboid6 bounding the render of this part for frustum culling. The
* bounds are relative to the tile coordinates.
*/
def getRenderBounds = Cuboid6.full
/** Write all the data required to describe a client version of this part to
* the packet. Called serverside, when a client loads this part for the first
* time.
*/
def writeDesc(packet: MCDataOutput) {}
/** Fill out this part with the description information contained in packet.
* Will be exactly as written from writeDesc. Called clientside when a client
* loads this part for the first time.
*/
def readDesc(packet: MCDataInput) {}
/** Save part to NBT (only called serverside)
*/
def save(tag: NBTTagCompound) {}
/** Load part from NBT (only called serverside)
*/
def load(tag: NBTTagCompound) {}
/** Gets a MCDataOutput instance for writing update data to clients with this
* part loaded. The write stream functions as a buffer which is flushed in a
* compressed databurst packet at the end of the tick.
*/
def getWriteStream: MCDataOutput = tile.getWriteStream(this)
/** Read and operate on data written to getWriteStream. Ensure all data this
* part wrote is read even if it's not going to be used. The default
* implementation assumes a call to sendDescUpdate as the only use of
* getWriteStream.
*/
def read(packet: MCDataInput) {
readDesc(packet)
tile.markRender()
}
/** Quick and easy method to re-describe the whole part on the client. This
* will call read on the client which calls readDesc unless overriden.
* Incremental changes should be sent rather than the whole description
* packet if possible.
*/
def sendDescUpdate() = writeDesc(getWriteStream)
/** Called when a part is added or removed from this block space. The part
* parameter may be null if several things have changed.
*/
def onPartChanged(part: TMultiPart) {}
/** Called when a neighbor block changed
*/
def onNeighborChanged() {}
/** Called when this part is added to the block space
*/
def onAdded() = onWorldJoin()
/** Called when this part is removed from the block space
*/
def onRemoved() = onWorldSeparate()
/** Called when the containing chunk is loaded on the server.
*/
def onChunkLoad() = onWorldJoin()
/** Called when the containing chunk is unloaded on the server.
*/
def onChunkUnload() = onWorldSeparate()
/** Called when this part separates from the world (due to removal, chunk
* unload or other). Use this to sync with external data structures. Called
* on both client and server
*/
def onWorldSeparate() {}
/** Called when this part joins the world (due to placement, chunkload or
* frame move etc). Use this to sync with external data structures. Called on
* both client and server
*/
def onWorldJoin() {}
/** Called when this part is converted from a normal block/tile (only
* applicable if a converter has been registered)
*/
def onConverted() = onAdded()
/** Called when this part is converted from a normal block/tile (only
* applicable if a converter has been registered) before the original tile
* has been replaced Use this to clear out things like inventory from the old
* tile.
*/
def invalidateConvertedTile() {}
/** Called when this part has been moved without a save/load.
*/
def onMoved() = onWorldJoin()
/** Called just before this part is actually removed from the container tile
*/
def preRemove() {}
/** Return whether this part needs update ticks. This will only be called on
* addition/removal so it should be a constant for this instance.
*/
def doesTick = true
/** Called once per world tick. This will be called even if doesTick returns
* false if another part in the space needs ticks.
*/
def update() {}
/** Called when a scheduled tick is executed.
*/
def scheduledTick() {}
/** Sets a scheduledTick callback for this part ticks in the future. This is a
* world time value, so if the chunk is unloaded and reloaded some time
* later, the tick may fire immediately.
*/
def scheduleTick(ticks: Int) = TickScheduler.scheduleTick(this, ticks)
/** Return the itemstack for the middle click pick-block function.
*/
def pickItem(hit: MovingObjectPosition): ItemStack = null
/** Called on block right click. item is the player's held item. This should
* not modify the part client side. If the client call returns false, the
* server will not call this function.
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
*/
def activate(
player: EntityPlayer,
hit: MovingObjectPosition,
item: ItemStack
) = false
/** Called on block left click. item is the player's held item.
* @param hit
* An instance of ExtendedMOP from collisionRayTrace
*/
def click(player: EntityPlayer, hit: MovingObjectPosition, item: ItemStack) {}
/** Called when an entity is within this block space. May not actually collide
* with this part.
*/
def onEntityCollision(entity: Entity) {}
}
@@ -4,65 +4,62 @@ import codechicken.lib.vec.Cuboid6
import scala.collection.JavaConversions._
import java.lang.Iterable
/**
* This suite of 3 classes provides simple functions for standard bounding box based occlusion testing.
* If any two parts have overlapping bounding boxes, the test fails
*
* See TIconHitEffects for notes on the Scala|Java composition setup.
*/
object NormalOcclusionTest
{
/**
* Performs the test, returns true if the test fails
*/
def apply(boxes1:Traversable[Cuboid6], boxes2:Traversable[Cuboid6]):Boolean =
boxes1.forall(v1 => boxes2.forall(v2 => !v1.intersects(v2)))
/**
* Performs the test, returns true if the test fails
*/
def apply(part1:JNormalOcclusion, part2:TMultiPart):Boolean =
{
var boxes = Seq[Cuboid6]()
if(part2.isInstanceOf[JNormalOcclusion])
boxes = boxes++part2.asInstanceOf[JNormalOcclusion].getOcclusionBoxes
if(part2.isInstanceOf[JPartialOcclusion])
boxes = boxes++part2.asInstanceOf[JPartialOcclusion].getPartialOcclusionBoxes
/** This suite of 3 classes provides simple functions for standard bounding box
* based occlusion testing. If any two parts have overlapping bounding boxes,
* the test fails
*
* See TIconHitEffects for notes on the Scala|Java composition setup.
*/
object NormalOcclusionTest {
return NormalOcclusionTest(boxes, part1.getOcclusionBoxes)
}
/** Performs the test, returns true if the test fails
*/
def apply(
boxes1: Traversable[Cuboid6],
boxes2: Traversable[Cuboid6]
): Boolean =
boxes1.forall(v1 => boxes2.forall(v2 => !v1.intersects(v2)))
/** Performs the test, returns true if the test fails
*/
def apply(part1: JNormalOcclusion, part2: TMultiPart): Boolean = {
var boxes = Seq[Cuboid6]()
if (part2.isInstanceOf[JNormalOcclusion])
boxes = boxes ++ part2.asInstanceOf[JNormalOcclusion].getOcclusionBoxes
if (part2.isInstanceOf[JPartialOcclusion])
boxes =
boxes ++ part2.asInstanceOf[JPartialOcclusion].getPartialOcclusionBoxes
return NormalOcclusionTest(boxes, part1.getOcclusionBoxes)
}
}
/**
* Java interface containing callbacks for normal occlusion testing.
* Make sure to override occlusionTest as in TNormalOcclusion
*/
trait JNormalOcclusion
{
/**
* Return a list of normal occlusion boxes
*/
def getOcclusionBoxes:Iterable[Cuboid6]
/** Java interface containing callbacks for normal occlusion testing. Make sure
* to override occlusionTest as in TNormalOcclusion
*/
trait JNormalOcclusion {
/** Return a list of normal occlusion boxes
*/
def getOcclusionBoxes: Iterable[Cuboid6]
}
/**
* Trait for scala programmers
*/
trait TNormalOcclusion extends TMultiPart with JNormalOcclusion
{
override def occlusionTest(npart:TMultiPart):Boolean =
NormalOcclusionTest(this, npart) && super.occlusionTest(npart)
/** Trait for scala programmers
*/
trait TNormalOcclusion extends TMultiPart with JNormalOcclusion {
override def occlusionTest(npart: TMultiPart): Boolean =
NormalOcclusionTest(this, npart) && super.occlusionTest(npart)
}
/**
* Utility part class for performing 3rd party occlusion tests
*/
class NormallyOccludedPart(bounds:Iterable[Cuboid6]) extends TMultiPart with TNormalOcclusion
{
def this(bound:Cuboid6) = this(Seq(bound))
def getType = null
def getOcclusionBoxes = bounds
}
/** Utility part class for performing 3rd party occlusion tests
*/
class NormallyOccludedPart(bounds: Iterable[Cuboid6])
extends TMultiPart
with TNormalOcclusion {
def this(bound: Cuboid6) = this(Seq(bound))
def getType = null
def getOcclusionBoxes = bounds
}
@@ -4,73 +4,70 @@ import codechicken.lib.vec.Cuboid6
import scala.collection.JavaConversions._
import java.lang.Iterable
/**
* This class provides a special type of occlusion model used by microblocks.
* The partial occlusion test defines bounding boxes that may intersect, so long as no part is completely obscured by a combination of the others.
* Partial bounding boxes may not intersect with normal bounding boxes from NormalOcclusionTest
*
* This test is actually managed by the mixin trait TPartialOcclusionTile which is generated when the marker interface JPartialOcclusion is found
*/
class PartialOcclusionTest(size:Int)
{
/**
* The resolution of the test, set to 1/8th of a block
*/
val res = 8
val bits = new Array[Byte](res*res*res)
val partial = new Array[Boolean](size)
def fill(i:Int, part:JPartialOcclusion)
{
fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion)
}
def fill(i:Int, boxes:Iterable[Cuboid6], complete:Boolean)
{
partial(i) = !complete
boxes.foreach(box => fill(i+1, box))
}
def fill(v:Int, box:Cuboid6)
{
for(x <- (box.min.x*res+0.5).toInt until (box.max.x*res+0.5).toInt)
for(y <- (box.min.y*res+0.5).toInt until (box.max.y*res+0.5).toInt)
for(z <- (box.min.z*res+0.5).toInt until (box.max.z*res+0.5).toInt)
{
val i = (x*res+y)*res+z
if(bits(i) == 0)
bits(i) = v.toByte
else
bits(i) = -1
}
}
def apply():Boolean =
{
val visible = new Array[Boolean](size)
bits.foreach(n => if(n > 0) visible(n-1) = true)
var i = 0
while(i < partial.length)
{
if(partial(i) && !visible(i))
return false
i+=1
/** This class provides a special type of occlusion model used by microblocks.
* The partial occlusion test defines bounding boxes that may intersect, so
* long as no part is completely obscured by a combination of the others.
* Partial bounding boxes may not intersect with normal bounding boxes from
* NormalOcclusionTest
*
* This test is actually managed by the mixin trait TPartialOcclusionTile which
* is generated when the marker interface JPartialOcclusion is found
*/
class PartialOcclusionTest(size: Int) {
/** The resolution of the test, set to 1/8th of a block
*/
val res = 8
val bits = new Array[Byte](res * res * res)
val partial = new Array[Boolean](size)
def fill(i: Int, part: JPartialOcclusion) {
fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion)
}
def fill(i: Int, boxes: Iterable[Cuboid6], complete: Boolean) {
partial(i) = !complete
boxes.foreach(box => fill(i + 1, box))
}
def fill(v: Int, box: Cuboid6) {
for (x <- (box.min.x * res + 0.5).toInt until (box.max.x * res + 0.5).toInt)
for (
y <- (box.min.y * res + 0.5).toInt until (box.max.y * res + 0.5).toInt
)
for (
z <- (box.min.z * res + 0.5).toInt until (box.max.z * res + 0.5).toInt
) {
val i = (x * res + y) * res + z
if (bits(i) == 0)
bits(i) = v.toByte
else
bits(i) = -1
}
return true
}
def apply(): Boolean = {
val visible = new Array[Boolean](size)
bits.foreach(n => if (n > 0) visible(n - 1) = true)
var i = 0
while (i < partial.length) {
if (partial(i) && !visible(i))
return false
i += 1
}
return true
}
}
trait JPartialOcclusion
{
/**
* Return a list of partial occlusion boxes
*/
def getPartialOcclusionBoxes:Iterable[Cuboid6]
/**
* Return true if this part may be completely obscured
*/
def allowCompleteOcclusion = false
}
trait JPartialOcclusion {
/** Return a list of partial occlusion boxes
*/
def getPartialOcclusionBoxes: Iterable[Cuboid6]
/** Return true if this part may be completely obscured
*/
def allowCompleteOcclusion = false
}
@@ -1,19 +1,19 @@
package codechicken.multipart
/**
* Interface for parts with random update ticks. Used in conjuction with TickScheduler
*/
trait TRandomUpdateTick extends TMultiPart
{
/**
* Called on random update. Random ticks are between 800 and 1600 ticks from their last scheduled/random tick
*/
def randomUpdate()
/** Interface for parts with random update ticks. Used in conjuction with
* TickScheduler
*/
trait TRandomUpdateTick extends TMultiPart {
/**
* If implementing interface in java, be sure to implement this method yourself
*/
override def onWorldJoin() {
TickScheduler.loadRandomTick(this)
}
}
/** Called on random update. Random ticks are between 800 and 1600 ticks from
* their last scheduled/random tick
*/
def randomUpdate()
/** If implementing interface in java, be sure to implement this method
* yourself
*/
override def onWorldJoin() {
TickScheduler.loadRandomTick(this)
}
}
@@ -1,15 +1,14 @@
package codechicken.multipart
/**
* Interface for parts that fill a slot based configuration as defined in PartMap.
* If this is implemented, calling partMap(slot) on the host tile will return this part if the corresponding bit in the slotMask is set
*
* Marker interface for TSlottedTile
*/
trait TSlottedPart extends TMultiPart
{
/**
* a bitmask of slots that this part fills. slot x is 1<<x
*/
def getSlotMask:Int
}
/** Interface for parts that fill a slot based configuration as defined in
* PartMap. If this is implemented, calling partMap(slot) on the host tile will
* return this part if the corresponding bit in the slotMask is set
*
* Marker interface for TSlottedTile
*/
trait TSlottedPart extends TMultiPart {
/** a bitmask of slots that this part fills. slot x is 1<<x
*/
def getSlotMask: Int
}
@@ -18,243 +18,242 @@ import java.io.FileOutputStream
import java.io.FileInputStream
import net.minecraft.world.storage.SaveHandler
/**
* Used for scheduling delayed callbacks to parts.
* Do not use this for redstone applications that require precise timing.
* If 2 parts are both scheduled for an update on the same tick, there is no guarantee which one will update first.
* These parts should not depend on a state of another part that may have changed before/after them.
*/
object TickScheduler extends WorldExtensionInstantiator
{
class PartTickEntry(val part:TMultiPart, var time:Long, var random:Boolean)
{
def this(part:TMultiPart, ticks:Int) = this(part, ticks, false)
}
private class WorldTickScheduler(world$:World) extends WorldExtension(world$)
{
var schedTime = 0L
var tickChunks = HashSet[ChunkTickScheduler]()
private var processing = false
private val pending = ListBuffer[PartTickEntry]()
def scheduleTick(part:TMultiPart, ticks:Int, random:Boolean)
{
if(processing)
pending+=new PartTickEntry(part, schedTime+ticks, random)
else
_scheduleTick(part, schedTime+ticks, random)
}
def _scheduleTick(part:TMultiPart, time:Long, random:Boolean)
{
if(part.tile != null)
getChunkExtension(part.tile.xCoord>>4, part.tile.zCoord>>4)
.asInstanceOf[ChunkTickScheduler].scheduleTick(part, time, random)
}
def loadRandom(part:TRandomUpdateTick) = scheduleTick(part, nextRandomTick, true)
override def preTick()
{
processing = true
}
override def postTick()
{
if(!tickChunks.isEmpty)
tickChunks = tickChunks.filter(_.processTicks())
processing = false
pending.foreach(e => _scheduleTick(e.part, e.time, e.random))
pending.clear()
schedTime+=1
}
def saveDir:File =
{
if(world.provider.dimensionId == 0)//Calling DimensionManager.getCurrentSaveRootDirectory too early breaks game saves, we have a world reference, use it
return world.getSaveHandler.asInstanceOf[SaveHandler].getWorldDirectory
return new File(DimensionManager.getCurrentSaveRootDirectory, world.provider.getSaveFolder)
}
def saveFile:File = new File(saveDir, "multipart.dat")
override def load()
{
try
{
val in = new FileInputStream(saveFile)
loadTag(CompressedStreamTools.readCompressed(in))
in.close()
}
catch
{
case e:Exception =>
}
loadTag(new NBTTagCompound)
}
def loadTag(tag:NBTTagCompound)
{
if(tag.hasKey("schedTime"))
schedTime = tag.getLong("schedTime")
else
schedTime = world.getTotalWorldTime
}
def saveTag:NBTTagCompound =
{
val tag = new NBTTagCompound
tag.setLong("schedTime", schedTime)
return tag
}
override def save() {
val file = saveFile
if(!file.getParentFile.exists)
file.getParentFile.mkdirs()
if(!file.exists)
file.createNewFile()
val dout = new DataOutputStream(new FileOutputStream(file))
CompressedStreamTools.writeCompressed(saveTag, dout)
dout.close()
}
def nextRandomTick = world.rand.nextInt(800)+800
}
def createWorldExtension(world:World):WorldExtension = new WorldTickScheduler(world)
private class ChunkTickScheduler(chunk$:Chunk, world:WorldTickScheduler) extends ChunkExtension(chunk$, world)
{
import codechicken.multipart.handler.MultipartProxy._
var tickList = ListBuffer[PartTickEntry]()
def schedTime = world.schedTime
def scheduleTick(part:TMultiPart, time:Long, random:Boolean)
{
val it = tickList.iterator
while(it.hasNext)
{
val e = it.next()
if(e.part == part)
{
if(e.random && !random)//only override an existing tick if we're going from random->scheduled
{
e.time = time
e.random = random
}
return
}
}
tickList+=new PartTickEntry(part, time, random)
if(tickList.size == 1)
world.tickChunks+=this
}
def nextRandomTick = world.nextRandomTick
def processTicks():Boolean =
{
tickList = tickList.filter(processTick)
return !tickList.isEmpty
}
def processTick(e:PartTickEntry):Boolean =
{
if(e.time <= schedTime)
{
if(e.part.tile != null)
{
if(e.random && e.part.isInstanceOf[TRandomUpdateTick])
e.part.asInstanceOf[TRandomUpdateTick].randomUpdate()
else
e.part.scheduledTick()
if(e.part.isInstanceOf[TRandomUpdateTick])
{
e.time = schedTime+nextRandomTick
e.random = true
return true
}
}
return false
}
return true
}
override def saveData(data:NBTTagCompound)
{
val tagList = new NBTTagList
tickList.foreach{e =>
val part = e.part
if(part.tile != null && !e.random)
{
val tag = new NBTTagCompound
tag.setShort("pos", indexInChunk(new BlockCoord(part.tile)).toShort)
tag.setByte("i", part.tile.partList.indexOf(part).toByte)
tag.setLong("time", e.time)
tagList.appendTag(tag)
}
}
if(tagList.tagCount > 0)
data.setTag("multipartTicks", tagList)
}
override def loadData(data:NBTTagCompound)
{
tickList.clear()
if(!data.hasKey("multipartTicks"))
return
val tagList = data.getTagList("multipartTicks", 10)
val cc = new ChunkCoordIntPair(0, 0)
for(i <- 0 until tagList.tagCount)
{
val tag = tagList.getCompoundTagAt(i)
val pos = indexInChunk(cc, tag.getShort("pos"))
val tile = chunk.chunkTileEntityMap.get(new ChunkPosition(pos.x, pos.y, pos.z))
if(tile.isInstanceOf[TileMultipart])
tickList+=new PartTickEntry(tile.asInstanceOf[TileMultipart].partList(tag.getByte("i")), tag.getLong("time"), false)
}
}
override def unload()
{
if(!tickList.isEmpty)
world.tickChunks-=this
}
}
def createChunkExtension(chunk:Chunk, world:WorldExtension):ChunkExtension = new ChunkTickScheduler(chunk, world.asInstanceOf[WorldTickScheduler])
/** Used for scheduling delayed callbacks to parts. Do not use this for redstone
* applications that require precise timing. If 2 parts are both scheduled for
* an update on the same tick, there is no guarantee which one will update
* first. These parts should not depend on a state of another part that may
* have changed before/after them.
*/
object TickScheduler extends WorldExtensionInstantiator {
class PartTickEntry(
val part: TMultiPart,
var time: Long,
var random: Boolean
) {
def this(part: TMultiPart, ticks: Int) = this(part, ticks, false)
}
/**
* Start random ticking for a part. Should be called from TMultiPart.onWorldJoin
*/
def loadRandomTick(part:TRandomUpdateTick)
{
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].loadRandom(part)
private class WorldTickScheduler(world$ : World)
extends WorldExtension(world$) {
var schedTime = 0L
var tickChunks = HashSet[ChunkTickScheduler]()
private var processing = false
private val pending = ListBuffer[PartTickEntry]()
def scheduleTick(part: TMultiPart, ticks: Int, random: Boolean) {
if (processing)
pending += new PartTickEntry(part, schedTime + ticks, random)
else
_scheduleTick(part, schedTime + ticks, random)
}
/**
* Schedule a tick for part relative to the current time.
*/
def scheduleTick(part:TMultiPart, ticks:Int)
{
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].scheduleTick(part, ticks, false)
def _scheduleTick(part: TMultiPart, time: Long, random: Boolean) {
if (part.tile != null)
getChunkExtension(part.tile.xCoord >> 4, part.tile.zCoord >> 4)
.asInstanceOf[ChunkTickScheduler]
.scheduleTick(part, time, random)
}
/**
* Returns the current scheduler time. Like the world time, but unaffected by the time set command and other things changing time of day.
* Deprecated in favor of world.getTotalWorldTime
*/
@Deprecated
def getSchedulerTime(world:World):Long = getExtension(world).asInstanceOf[WorldTickScheduler].schedTime
}
def loadRandom(part: TRandomUpdateTick) =
scheduleTick(part, nextRandomTick, true)
override def preTick() {
processing = true
}
override def postTick() {
if (!tickChunks.isEmpty)
tickChunks = tickChunks.filter(_.processTicks())
processing = false
pending.foreach(e => _scheduleTick(e.part, e.time, e.random))
pending.clear()
schedTime += 1
}
def saveDir: File = {
if (
world.provider.dimensionId == 0
) // Calling DimensionManager.getCurrentSaveRootDirectory too early breaks game saves, we have a world reference, use it
return world.getSaveHandler.asInstanceOf[SaveHandler].getWorldDirectory
return new File(
DimensionManager.getCurrentSaveRootDirectory,
world.provider.getSaveFolder
)
}
def saveFile: File = new File(saveDir, "multipart.dat")
override def load() {
try {
val in = new FileInputStream(saveFile)
loadTag(CompressedStreamTools.readCompressed(in))
in.close()
} catch {
case e: Exception =>
}
loadTag(new NBTTagCompound)
}
def loadTag(tag: NBTTagCompound) {
if (tag.hasKey("schedTime"))
schedTime = tag.getLong("schedTime")
else
schedTime = world.getTotalWorldTime
}
def saveTag: NBTTagCompound = {
val tag = new NBTTagCompound
tag.setLong("schedTime", schedTime)
return tag
}
override def save() {
val file = saveFile
if (!file.getParentFile.exists)
file.getParentFile.mkdirs()
if (!file.exists)
file.createNewFile()
val dout = new DataOutputStream(new FileOutputStream(file))
CompressedStreamTools.writeCompressed(saveTag, dout)
dout.close()
}
def nextRandomTick = world.rand.nextInt(800) + 800
}
def createWorldExtension(world: World): WorldExtension =
new WorldTickScheduler(world)
private class ChunkTickScheduler(chunk$ : Chunk, world: WorldTickScheduler)
extends ChunkExtension(chunk$, world) {
import codechicken.multipart.handler.MultipartProxy._
var tickList = ListBuffer[PartTickEntry]()
def schedTime = world.schedTime
def scheduleTick(part: TMultiPart, time: Long, random: Boolean) {
val it = tickList.iterator
while (it.hasNext) {
val e = it.next()
if (e.part == part) {
if (
e.random && !random
) // only override an existing tick if we're going from random->scheduled
{
e.time = time
e.random = random
}
return
}
}
tickList += new PartTickEntry(part, time, random)
if (tickList.size == 1)
world.tickChunks += this
}
def nextRandomTick = world.nextRandomTick
def processTicks(): Boolean = {
tickList = tickList.filter(processTick)
return !tickList.isEmpty
}
def processTick(e: PartTickEntry): Boolean = {
if (e.time <= schedTime) {
if (e.part.tile != null) {
if (e.random && e.part.isInstanceOf[TRandomUpdateTick])
e.part.asInstanceOf[TRandomUpdateTick].randomUpdate()
else
e.part.scheduledTick()
if (e.part.isInstanceOf[TRandomUpdateTick]) {
e.time = schedTime + nextRandomTick
e.random = true
return true
}
}
return false
}
return true
}
override def saveData(data: NBTTagCompound) {
val tagList = new NBTTagList
tickList.foreach { e =>
val part = e.part
if (part.tile != null && !e.random) {
val tag = new NBTTagCompound
tag.setShort("pos", indexInChunk(new BlockCoord(part.tile)).toShort)
tag.setByte("i", part.tile.partList.indexOf(part).toByte)
tag.setLong("time", e.time)
tagList.appendTag(tag)
}
}
if (tagList.tagCount > 0)
data.setTag("multipartTicks", tagList)
}
override def loadData(data: NBTTagCompound) {
tickList.clear()
if (!data.hasKey("multipartTicks"))
return
val tagList = data.getTagList("multipartTicks", 10)
val cc = new ChunkCoordIntPair(0, 0)
for (i <- 0 until tagList.tagCount) {
val tag = tagList.getCompoundTagAt(i)
val pos = indexInChunk(cc, tag.getShort("pos"))
val tile =
chunk.chunkTileEntityMap.get(new ChunkPosition(pos.x, pos.y, pos.z))
if (tile.isInstanceOf[TileMultipart])
tickList += new PartTickEntry(
tile.asInstanceOf[TileMultipart].partList(tag.getByte("i")),
tag.getLong("time"),
false
)
}
}
override def unload() {
if (!tickList.isEmpty)
world.tickChunks -= this
}
}
def createChunkExtension(
chunk: Chunk,
world: WorldExtension
): ChunkExtension =
new ChunkTickScheduler(chunk, world.asInstanceOf[WorldTickScheduler])
/** Start random ticking for a part. Should be called from
* TMultiPart.onWorldJoin
*/
def loadRandomTick(part: TRandomUpdateTick) {
getExtension(part.tile.getWorldObj)
.asInstanceOf[WorldTickScheduler]
.loadRandom(part)
}
/** Schedule a tick for part relative to the current time.
*/
def scheduleTick(part: TMultiPart, ticks: Int) {
getExtension(part.tile.getWorldObj)
.asInstanceOf[WorldTickScheduler]
.scheduleTick(part, ticks, false)
}
/** Returns the current scheduler time. Like the world time, but unaffected by
* the time set command and other things changing time of day. Deprecated in
* favor of world.getTotalWorldTime
*/
@Deprecated
def getSchedulerTime(world: World): Long =
getExtension(world).asInstanceOf[WorldTickScheduler].schedTime
}
@@ -5,34 +5,42 @@ import scala.collection.mutable.{Map => MMap}
import net.minecraft.world.World
import codechicken.multipart.handler.MultipartProxy
/**
* In order to maintain tight client/server synchronisation without bandwidth overhead, all data written must be read.
* Sometimes the client tile is removed from the chunk before the packet arrives. This class maintains a reference to all multipart tiles.
* The original design placed tiles that were invalidated into a map that was cleared upon arrival of the update packet.
* Due to other factors that have eluded identification, references to all tiles held until they are replaced or the world is reloaded.
*/
object TileCache
{
case class FlaggedTile(t:TileMultipart, removed:Boolean)
/** In order to maintain tight client/server synchronisation without bandwidth
* overhead, all data written must be read. Sometimes the client tile is
* removed from the chunk before the packet arrives. This class maintains a
* reference to all multipart tiles. The original design placed tiles that were
* invalidated into a map that was cleared upon arrival of the update packet.
* Due to other factors that have eluded identification, references to all
* tiles held until they are replaced or the world is reloaded.
*/
object TileCache {
case class FlaggedTile(t: TileMultipart, removed: Boolean)
val map = MMap[BlockCoord, FlaggedTile]()
val map = MMap[BlockCoord, FlaggedTile]()
def add(t:TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, false))
def remove(t:TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, true))
def apply(c:BlockCoord) = map.get(c)
def clear() = map.clear()
def add(t: TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, false))
def remove(t: TileMultipart) =
map.put(new BlockCoord(t), FlaggedTile(t, true))
def apply(c: BlockCoord) = map.get(c)
def clear() = map.clear()
def findTile(world:World, c:BlockCoord) = BlockMultipart.getTile(world, c.x, c.y, c.z) match {
case null => apply(c) match {
case Some(FlaggedTile(t, rem)) =>
if(!rem)
MultipartProxy.logger.warn("Client multipart @"+c+" vanished from world but was recovered. If possible causes can be identified, please report to the github issue tracker.")
t
case None =>
throw new RuntimeException("DC: Client multipart @"+c+" not found")
case _ =>
null
def findTile(world: World, c: BlockCoord) =
BlockMultipart.getTile(world, c.x, c.y, c.z) match {
case null =>
apply(c) match {
case Some(FlaggedTile(t, rem)) =>
if (!rem)
MultipartProxy.logger.warn(
"Client multipart @" + c + " vanished from world but was recovered. If possible causes can be identified, please report to the github issue tracker."
)
t
case None =>
throw new RuntimeException(
"DC: Client multipart @" + c + " not found"
)
case _ =>
null
}
case t => t
case t => t
}
}
File diff suppressed because it is too large. Load diff
@@ -2,23 +2,21 @@ package codechicken.multipart.asm
import java.util.BitSet
object ASMImplicits
{
implicit class ExtBitSet(val bitset:BitSet) extends AnyVal
{
def set(b:BitSet) = {
bitset.clear()
bitset.or(b)
bitset
}
def copy = new BitSet().set(bitset)
object ASMImplicits {
implicit class ExtBitSet(val bitset: BitSet) extends AnyVal {
def set(b: BitSet) = {
bitset.clear()
bitset.or(b)
bitset
}
def nodeName(name:String) = if(name == null) null else name.replace('.', '/')
def copy = new BitSet().set(bitset)
}
implicit class ExtClass(val clazz:Class[_]) extends AnyVal
{
def nodeName = ASMImplicits.nodeName(clazz.getName)
}
def nodeName(name: String) =
if (name == null) null else name.replace('.', '/')
implicit class ExtClass(val clazz: Class[_]) extends AnyVal {
def nodeName = ASMImplicits.nodeName(clazz.getName)
}
}
File diff suppressed because it is too large. Load diff
@@ -8,80 +8,88 @@ import scala.collection.mutable.ArrayBuffer
import ASMMixinCompiler._
import ASMImplicits._
class ASMMixinFactory[T](val baseType:Class[T], private val paramTypes:Class[_]*)
{
private val traitMap = mutable.Map[String, Int]()
private val traits = ArrayBuffer[String]()
private val classMap = mutable.Map[BitSet, Constructor[_ <: T]]()
class ASMMixinFactory[T](
val baseType: Class[T],
private val paramTypes: Class[_]*
) {
private val traitMap = mutable.Map[String, Int]()
private val traits = ArrayBuffer[String]()
private val classMap = mutable.Map[BitSet, Constructor[_ <: T]]()
private var ugenid = 0
private var ugenid = 0
private def nextName() = {
val ret = baseType.getSimpleName+"_cmp$$"+ugenid
ugenid += 1
ret
private def nextName() = {
val ret = baseType.getSimpleName + "_cmp$$" + ugenid
ugenid += 1
ret
}
private def compile(traitSet: BitSet) = {
val seq = Seq.newBuilder[String]
var i = -1
while ({ i = traitSet.nextSetBit(i + 1); i >= 0 })
seq += traits(i)
val c = ASMMixinCompiler
.mixinClasses(nextName(), baseType.nodeName, seq.result())
.asInstanceOf[Class[_ <: T]]
onCompiled(c, traitSet)
c.getDeclaredConstructor(paramTypes: _*)
}
protected def onCompiled(clazz: Class[_ <: T], traitSet: BitSet) {}
protected def autoCompleteJavaTrait(cnode: ClassNode) {}
def construct(traitSet: BitSet, args: Object*) = synchronized {
(classMap.get(traitSet) match {
case Some(c) => c
case None =>
val c = compile(traitSet)
classMap.put(traitSet.copy, c)
c
}).newInstance(args: _*)
}
def getId(s_trait: String) = traitMap(s_trait)
def registerTrait(traitClass: Class[_]): Int = registerTrait(
traitClass.nodeName
)
def registerTrait(s_trait: String): Int = {
val cnode = classNode(s_trait)
if (cnode == null)
throw new ClassNotFoundException(s_trait)
traitMap.get(cnode.name) match {
case Some(id) => return id
case None =>
}
private def compile(traitSet:BitSet) = {
val seq = Seq.newBuilder[String]
var i = -1
while({i = traitSet.nextSetBit(i+1); i >= 0})
seq += traits(i)
val info = getClassInfo(cnode)
def concreteParent(info: ClassInfo): ClassInfo = info.superClass.map {
case i if i.isTrait => concreteParent(i)
case i => i
}.get
val c = ASMMixinCompiler.mixinClasses(nextName(), baseType.nodeName, seq.result()).asInstanceOf[Class[_ <: T]]
onCompiled(c, traitSet)
c.getDeclaredConstructor(paramTypes:_*)
val parentName = concreteParent(info).name
def checkParent(info: ClassInfo): Boolean =
info.name == parentName || info.superClass.exists(checkParent)
if (!checkParent(getClassInfo(baseType)))
throw new IllegalArgumentException(
baseType.nodeName + " does not extend parent " + parentName + " of mixin trait " + s_trait
)
if (info.isTrait) {
registerScalaTrait(cnode)
} else {
autoCompleteJavaTrait(cnode)
registerJavaTrait(cnode)
}
protected def onCompiled(clazz:Class[_ <: T], traitSet:BitSet){}
protected def autoCompleteJavaTrait(cnode:ClassNode){}
def construct(traitSet:BitSet, args:Object*) = synchronized {
(classMap.get(traitSet) match {
case Some(c) => c
case None =>
val c = compile(traitSet)
classMap.put(traitSet.copy, c)
c
}).newInstance(args: _*)
}
def getId(s_trait:String) = traitMap(s_trait)
def registerTrait(traitClass:Class[_]):Int = registerTrait(traitClass.nodeName)
def registerTrait(s_trait:String):Int = {
val cnode = classNode(s_trait)
if(cnode == null)
throw new ClassNotFoundException(s_trait)
traitMap.get(cnode.name) match {
case Some(id) => return id
case None =>
}
val info = getClassInfo(cnode)
def concreteParent(info:ClassInfo):ClassInfo = info.superClass.map {
case i if i.isTrait => concreteParent(i)
case i => i
}.get
val parentName = concreteParent(info).name
def checkParent(info:ClassInfo):Boolean = info.name == parentName || info.superClass.exists(checkParent)
if(!checkParent(getClassInfo(baseType)))
throw new IllegalArgumentException(baseType.nodeName+" does not extend parent "+parentName+" of mixin trait "+s_trait)
if(info.isTrait) {
registerScalaTrait(cnode)
}
else {
autoCompleteJavaTrait(cnode)
registerJavaTrait(cnode)
}
val id = traits.size
traits += cnode.name
traitMap.put(cnode.name, id)
id
}
val id = traits.size
traits += cnode.name
traitMap.put(cnode.name, id)
id
}
}
@@ -190,24 +190,22 @@ object ByteCodecs {
def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs))
/**
* Destructively decodes array xs and returns the length of the decoded array.
*
* Sometimes returns (length+1) of the decoded array. Example:
*
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
* enc: Array[Byte] = Array(2, 5, 13, 1)
*
* scala> scala.reflect.generic.ByteCodecs.decode(enc)
* res43: Int = 4
*
* scala> enc
* res44: Array[Byte] = Array(1, 2, 3, 0)
*
* However, this does not always happen.
*/
/** Destructively decodes array xs and returns the length of the decoded
* array.
*
* Sometimes returns (length+1) of the decoded array. Example:
*
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
* enc: Array[Byte] = Array(2, 5, 13, 1)
*
* scala> scala.reflect.generic.ByteCodecs.decode(enc) res43: Int = 4
*
* scala> enc res44: Array[Byte] = Array(1, 2, 3, 0)
*
* However, this does not always happen.
*/
def decode(xs: Array[Byte]): Int = {
val len = regenerateZero(xs)
decode7to8(xs, len)
}
}
}
@@ -12,166 +12,237 @@ import ASMImplicits._
import scala.collection.JavaConversions._
object MultipartMixinFactory extends ASMMixinFactory(classOf[TileMultipart])
{
override protected def autoCompleteJavaTrait(cnode: ClassNode)
object MultipartMixinFactory extends ASMMixinFactory(classOf[TileMultipart]) {
override protected def autoCompleteJavaTrait(cnode: ClassNode) {
if (
!cnode.fields.isEmpty && findMethod(
new ObfMapping(
cnode.name,
"copyFrom",
"(Lcodechicken/multipart/TileMultipart;)V"
),
cnode
) == null
) {
val mv = cnode.visitMethod(
ACC_PUBLIC,
"copyFrom",
"(Lcodechicken/multipart/TileMultipart;)V",
null,
null
)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(
INVOKESPECIAL,
"codechicken/multipart/TileMultipart",
"copyFrom",
"(Lcodechicken/multipart/TileMultipart;)V"
)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, cnode.name)
val end = new Label()
mv.visitJumpInsn(IFEQ, end)
cnode.fields.foreach { f =>
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
}
mv.visitLabel(end)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
}
}
def generatePassThroughTrait(s_interface: String): String = {
val iname = nodeName(s_interface)
val tname = {
var simpleName = iname.substring(iname.lastIndexOf('/') + 1)
if (simpleName.startsWith("I")) simpleName = simpleName.substring(1)
"T" + simpleName + "$$PassThrough"
}
val vname = "impl"
val idesc = "L" + iname + ";"
val inode = classNode(s_interface)
if (inode == null) {
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.")
val cw = new CC_ClassWriter(0)
var mv: MethodVisitor = null
var fv: FieldVisitor = null
cw.visit(
V1_6,
ACC_PUBLIC | ACC_SUPER,
tname,
null,
"codechicken/multipart/TileMultipart",
Array(iname)
)
{
if(!cnode.fields.isEmpty && findMethod(new ObfMapping(cnode.name, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V"), cnode) == null)
{
val mv = cnode.visitMethod(ACC_PUBLIC, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, cnode.name)
val end = new Label()
mv.visitJumpInsn(IFEQ, end)
cnode.fields.foreach{ f =>
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
}
mv.visitLabel(end)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
}
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
fv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(
INVOKESPECIAL,
"codechicken/multipart/TileMultipart",
"<init>",
"()V"
)
mv.visitInsn(RETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
}
{
mv = cw.visitMethod(
ACC_PUBLIC,
"bindPart",
"(Lcodechicken/multipart/TMultiPart;)V",
null,
null
)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(
INVOKESPECIAL,
"codechicken/multipart/TileMultipart",
"bindPart",
"(Lcodechicken/multipart/TMultiPart;)V"
)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
val l2 = new Label()
mv.visitJumpInsn(IFEQ, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(CHECKCAST, iname)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
{
mv = cw.visitMethod(
ACC_PUBLIC,
"partRemoved",
"(Lcodechicken/multipart/TMultiPart;I)V",
null,
null
)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ILOAD, 2)
mv.visitMethodInsn(
INVOKESPECIAL,
"codechicken/multipart/TileMultipart",
"partRemoved",
"(Lcodechicken/multipart/TMultiPart;I)V"
)
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l2 = new Label()
mv.visitJumpInsn(IF_ACMPNE, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitInsn(ACONST_NULL)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(3, 3)
mv.visitEnd()
}
{
mv = cw.visitMethod(
ACC_PUBLIC,
"canAddPart",
"(Lcodechicken/multipart/TMultiPart;)Z",
null,
null
)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l1 = new Label()
mv.visitJumpInsn(IFNULL, l1)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
mv.visitJumpInsn(IFEQ, l1)
val l2 = new Label()
mv.visitLabel(l2)
mv.visitInsn(ICONST_0)
mv.visitInsn(IRETURN)
mv.visitLabel(l1)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(
INVOKESPECIAL,
"codechicken/multipart/TileMultipart",
"canAddPart",
"(Lcodechicken/multipart/TMultiPart;)Z"
)
mv.visitInsn(IRETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
def generatePassThroughTrait(s_interface:String):String =
{
val iname = nodeName(s_interface)
val tname = {
var simpleName = iname.substring(iname.lastIndexOf('/')+1)
if(simpleName.startsWith("I")) simpleName = simpleName.substring(1)
"T" + simpleName + "$$PassThrough"
}
val vname = "impl"
val idesc = "L"+iname+";"
val inode = classNode(s_interface)
if(inode == null) {
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.")
val cw = new CC_ClassWriter(0)
var mv:MethodVisitor = null
var fv:FieldVisitor = null
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, tname, null, "codechicken/multipart/TileMultipart", Array(iname))
{
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
fv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "<init>", "()V")
mv.visitInsn(RETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "bindPart", "(Lcodechicken/multipart/TMultiPart;)V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "bindPart", "(Lcodechicken/multipart/TMultiPart;)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
val l2 = new Label()
mv.visitJumpInsn(IFEQ, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(CHECKCAST, iname)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ILOAD, 2)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l2 = new Label()
mv.visitJumpInsn(IF_ACMPNE, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitInsn(ACONST_NULL)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(3, 3)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l1 = new Label()
mv.visitJumpInsn(IFNULL, l1)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
mv.visitJumpInsn(IFEQ, l1)
val l2 = new Label()
mv.visitLabel(l2)
mv.visitInsn(ICONST_0)
mv.visitInsn(IRETURN)
mv.visitLabel(l1)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z")
mv.visitInsn(IRETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
def methods(cnode:ClassNode):Map[String, MethodNode] =
{
val m = cnode.methods.map(m => (m.name+m.desc, m)).toMap
if(cnode.interfaces != null)
m++cnode.interfaces.flatMap(i => methods(classNode(i)))
else
m
}
def generatePassThroughMethod(m:MethodNode)
{
mv = cw.visitMethod(ACC_PUBLIC, m.name, m.desc, m.signature, Array(m.exceptions:_*))
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
}
methods(inode).values.foreach(generatePassThroughMethod)
cw.visitEnd()
internalDefine(tname, cw.toByteArray)
registerTrait(tname)
return tname
def methods(cnode: ClassNode): Map[String, MethodNode] = {
val m = cnode.methods.map(m => (m.name + m.desc, m)).toMap
if (cnode.interfaces != null)
m ++ cnode.interfaces.flatMap(i => methods(classNode(i)))
else
m
}
override protected def onCompiled(clazz: Class[_ <: TileMultipart], traitSet:BitSet) =
MultipartGenerator.registerTileClass(clazz, traitSet)
def generatePassThroughMethod(m: MethodNode) {
mv = cw.visitMethod(
ACC_PUBLIC,
m.name,
m.desc,
m.signature,
Array(m.exceptions: _*)
)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
}
methods(inode).values.foreach(generatePassThroughMethod)
cw.visitEnd()
internalDefine(tname, cw.toByteArray)
registerTrait(tname)
return tname
}
override protected def onCompiled(
clazz: Class[_ <: TileMultipart],
traitSet: BitSet
) =
MultipartGenerator.registerTileClass(clazz, traitSet)
}
@@ -10,344 +10,360 @@ import scala.collection.JavaConversions._
import ScalaSignature._
object ScalaSignature
{
object Bytes
{
def apply(arr: Array[Byte]):Bytes = Bytes(arr, 0, arr.length)
}
object ScalaSignature {
object Bytes {
def apply(arr: Array[Byte]): Bytes = Bytes(arr, 0, arr.length)
}
case class Bytes(arr: Array[Byte], pos: Int, len: Int)
{
def reader = new ByteCodeReader(this)
def section = arr.drop(pos).take(len)
}
case class Bytes(arr: Array[Byte], pos: Int, len: Int) {
def reader = new ByteCodeReader(this)
def section = arr.drop(pos).take(len)
}
trait Flags
{
def hasFlag(flag: Int): Boolean
trait Flags {
def hasFlag(flag: Int): Boolean
def isPrivate = hasFlag(0x00000004)
def isProtected = hasFlag(0x00000008)
def isAbstract = hasFlag(0x00000080)
def isDeferred = hasFlag(0x00000100)
//abstract for methods
def isMethod = hasFlag(0x00000200)
def isModule = hasFlag(0x00000400)
//object module class
def isInterface = hasFlag(0x00000800)
def isParam = hasFlag(0x00002000)
def isStatic = hasFlag(0x00800000)
def isTrait = hasFlag(0x02000000)
def isAccessor = hasFlag(0x08000000)
}
def isPrivate = hasFlag(0x00000004)
def isProtected = hasFlag(0x00000008)
def isAbstract = hasFlag(0x00000080)
def isDeferred = hasFlag(0x00000100)
// abstract for methods
def isMethod = hasFlag(0x00000200)
def isModule = hasFlag(0x00000400)
// object module class
def isInterface = hasFlag(0x00000800)
def isParam = hasFlag(0x00002000)
def isStatic = hasFlag(0x00800000)
def isTrait = hasFlag(0x02000000)
def isAccessor = hasFlag(0x08000000)
}
}
class ScalaSignature(val bytes: Bytes)
{
val major = bytes.arr(0).toInt
val minor = bytes.arr(1).toInt
val table = {
val bcr = bytes.reader
bcr.pos = 2
Array.tabulate(bcr.readNat){ i =>
val start = bcr.pos
val tpe = bcr.readByte
val len = bcr.readNat
bcr.advance(len)(new SigEntry(i, start, Bytes(bytes.arr, bcr.pos, len)))
}
class ScalaSignature(val bytes: Bytes) {
val major = bytes.arr(0).toInt
val minor = bytes.arr(1).toInt
val table = {
val bcr = bytes.reader
bcr.pos = 2
Array.tabulate(bcr.readNat) { i =>
val start = bcr.pos
val tpe = bcr.readByte
val len = bcr.readNat
bcr.advance(len)(new SigEntry(i, start, Bytes(bytes.arr, bcr.pos, len)))
}
}
trait SymbolRef extends Flags {
def full: String
def flags: Int
def hasFlag(flag: Int) = (flags & flag) != 0
}
trait ClassSymbolRef extends SymbolRef {
def name: String
def owner: SymbolRef
def flags: Int
def infoId: Int
def full = owner.full + "." + name
override def toString = getClass.getName.replaceAll(".+\\$", "") +
"(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
def isObject = false
def info: ClassType = evalT(infoId)
def jParent = info.parent.jName
def jInterfaces = info.interfaces.map(_.jName)
}
case class ClassSymbol(
name: String,
owner: SymbolRef,
flags: Int,
infoId: Int
) extends ClassSymbolRef
case class ObjectSymbol(
name: String,
owner: SymbolRef,
flags: Int,
infoId: Int
) extends ClassSymbolRef {
override def isObject = true
}
case class MethodSymbol(
name: String,
owner: SymbolRef,
flags: Int,
infoId: Int
) extends SymbolRef {
override def toString =
"MethodSymbol(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
def full = owner.full + "." + name
def info: TMethodType = evalT(infoId)
def jDesc = info.jDesc
}
case class ExternalSymbol(name: String) extends SymbolRef {
override def toString = name
def full = name
def flags = 0
}
case object NoSymbol extends SymbolRef {
def full = "<no symbol>"
def flags = 0
}
trait TMethodType {
def jDesc = "(" + params
.map(m => m.info.returnType.jDesc)
.mkString + ")" + returnType.jDesc
def returnType: TypeRef
def params: List[MethodSymbol]
}
case class ClassType(owner: SymbolRef, parents: List[TypeRef]) {
def parent = parents.head
def interfaces = parents.drop(1)
}
case class MethodType(returnType: TypeRef, params: List[MethodSymbol])
extends TMethodType
case class ParameterlessType(returnType: TypeRef) extends TMethodType {
def params = List()
}
trait TypeRef {
def sym: SymbolRef
def name = sym.full
def jName = name.replace('.', '/') match {
case "scala/AnyRef" | "scala/Any" => "java/lang/Object"
case s => s
}
trait SymbolRef extends Flags
{
def full: String
def flags: Int
def hasFlag(flag: Int) = (flags & flag) != 0
def jDesc = name match {
case "scala.Array" => null
case "scala.Long" => "J"
case "scala.Int" => "I"
case "scala.Short" => "S"
case "scala.Byte" => "B"
case "scala.Double" => "D"
case "scala.Float" => "F"
case "scala.Boolean" => "Z"
case "scala.Unit" => "V"
case _ => "L" + jName + ";"
}
}
trait ClassSymbolRef extends SymbolRef
{
def name: String
def owner: SymbolRef
def flags: Int
def infoId: Int
case class TypeRefType(owner: TypeRef, sym: SymbolRef, typArgs: List[TypeRef])
extends TMethodType
with TypeRef {
def params = List()
def full = owner.full + "." + name
override def toString = getClass.getName.replaceAll(".+\\$", "") +
"(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
def returnType = this
def isObject = false
def info: ClassType = evalT(infoId)
def jParent = info.parent.jName
def jInterfaces = info.interfaces.map(_.jName)
override def jDesc = name match {
case "scala.Array" => "[" + typArgs(0).jDesc
case _ => super.jDesc
}
}
case class ClassSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends ClassSymbolRef
case class ThisType(sym: SymbolRef) extends TypeRef
case class ObjectSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends ClassSymbolRef
{
override def isObject = true
case class SingleType(owner: TypeRef, sym: SymbolRef) extends TypeRef {
override def jName = super.jName + "$"
}
case object NoType extends TypeRef {
def sym = null
override def name = "<no type>"
}
case class SigEntry(index: Int, start: Int, bytes: Bytes) {
def id = bytes.arr(start)
def delete() = bytes.arr(start) = 3
override def toString =
"SigEntry(" + index + "," + id + "," + bytes.len + " bytes)"
}
trait Literal {
def value: Any
}
case class BooleanLiteral(value: Boolean) extends Literal
case class ByteLiteral(value: Byte) extends Literal
case class ShortLiteral(value: Short) extends Literal
case class CharLiteral(value: Char) extends Literal
case class IntLiteral(value: Int) extends Literal
case class LongLiteral(value: Long) extends Literal
case class FloatLiteral(value: Float) extends Literal
case class DoubleLiteral(value: Double) extends Literal
case object NullLiteral extends Literal {
override def value = null
}
case class StringLiteral(value: String) extends Literal
case class TypeLiteral(value: TypeRef) extends Literal
case class EnumLiteral(value: ExternalSymbol) extends Literal
case class ArrayLiteral(value: List[_]) extends Literal
case class AnnotationInfo(
owner: SymbolRef,
annType: TypeRef,
values: Map[String, Literal]
) {
def getValue[T](name: String) = values(name).asInstanceOf[T]
}
def evalS(i: Int): String = {
val e = table(i)
val bc = e.bytes
val bcr = bc.reader
e.id match {
case 1 | 2 => bcr.readString(bc.len)
case 3 => NoSymbol.full
case 9 | 10 =>
var s = evalS(bcr.readNat)
if (bc.pos + bc.len > bcr.pos)
s = evalS(bcr.readNat) + "." + s
s
}
}
case class MethodSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends SymbolRef
{
override def toString = "MethodSymbol(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
def full = owner.full + "." + name
def evalT[T](i: Int) = eval(i).asInstanceOf[T]
def info: TMethodType = evalT(infoId)
def jDesc = info.jDesc
def evalList[T](bcr: ByteCodeReader) = {
val l = List.newBuilder
while (bcr.more)
l += evalT(bcr.readNat)
l.result()
}
def eval(i: Int): Any = {
// only parse the ones that matter for this project
val e = table(i)
val bcr = e.bytes.reader
def nat = bcr.readNat
def evalS = this.evalS(nat)
def evalT[T] = this.evalT[T](nat)
def evalList[T] = this.evalList[T](bcr)
e.id match {
case 1 | 2 => this.evalS(i)
case 3 => NoSymbol
case 6 => ClassSymbol(evalS, evalT, nat, nat)
case 7 => ObjectSymbol(evalS, evalT, nat, nat)
case 8 => MethodSymbol(evalS, evalT, nat, nat)
case 9 | 10 => ExternalSymbol(this.evalS(i))
case 11 | 12 => NoType // 12 is actually NoPrefixType (no lower bound)
case 13 => ThisType(evalT)
case 14 => SingleType(evalT, evalT)
case 16 => TypeRefType(evalT, evalT, evalList)
case 19 => ClassType(evalT, evalList)
case 20 => MethodType(evalT, evalList)
case 21 | 48 =>
ParameterlessType(
evalT
) // 48 is actually a bounded super type, but it should work fine for this project
case 25 => BooleanLiteral(bcr.readLong != 0)
case 26 => ByteLiteral(bcr.readLong.toByte)
case 27 => ShortLiteral(bcr.readLong.toShort)
case 28 => CharLiteral(bcr.readLong.toChar)
case 29 => IntLiteral(bcr.readLong.toInt)
case 30 => LongLiteral(bcr.readLong)
case 31 =>
FloatLiteral(java.lang.Float.intBitsToFloat(bcr.readLong.toInt))
case 32 => DoubleLiteral(java.lang.Double.longBitsToDouble(bcr.readLong))
case 33 => StringLiteral(evalS)
case 34 => NullLiteral
case 35 => TypeLiteral(evalT)
case 36 => EnumLiteral(evalT)
case 40 =>
AnnotationInfo(
evalT,
evalT,
evalList.grouped(2).map(g => (g(0), g(1))).toMap
)
case 44 => ArrayLiteral(evalList)
case _ => e
}
}
case class ExternalSymbol(name: String) extends SymbolRef
{
override def toString = name
def full = name
def flags = 0
}
def collect[T](id: Int) = (0 until table.length).collect {
case i if table(i).id == id => evalT(i): T
}
case object NoSymbol extends SymbolRef
{
def full = "<no symbol>"
def flags = 0
}
trait TMethodType
{
def jDesc = "(" + params.map(m => m.info.returnType.jDesc).mkString + ")" + returnType.jDesc
def returnType: TypeRef
def params: List[MethodSymbol]
}
case class ClassType(owner: SymbolRef, parents: List[TypeRef])
{
def parent = parents.head
def interfaces = parents.drop(1)
}
case class MethodType(returnType: TypeRef, params: List[MethodSymbol]) extends TMethodType
case class ParameterlessType(returnType: TypeRef) extends TMethodType
{
def params = List()
}
trait TypeRef
{
def sym: SymbolRef
def name = sym.full
def jName = name.replace('.', '/') match {
case "scala/AnyRef" | "scala/Any" => "java/lang/Object"
case s => s
}
def jDesc = name match {
case "scala.Array" => null
case "scala.Long" => "J"
case "scala.Int" => "I"
case "scala.Short" => "S"
case "scala.Byte" => "B"
case "scala.Double" => "D"
case "scala.Float" => "F"
case "scala.Boolean" => "Z"
case "scala.Unit" => "V"
case _ => "L" + jName + ";"
}
}
case class TypeRefType(owner: TypeRef, sym: SymbolRef, typArgs: List[TypeRef]) extends TMethodType with TypeRef
{
def params = List()
def returnType = this
override def jDesc = name match {
case "scala.Array" => "[" + typArgs(0).jDesc
case _ => super.jDesc
}
}
case class ThisType(sym: SymbolRef) extends TypeRef
case class SingleType(owner: TypeRef, sym: SymbolRef) extends TypeRef
{
override def jName = super.jName + "$"
}
case object NoType extends TypeRef
{
def sym = null
override def name = "<no type>"
}
case class SigEntry(index:Int, start: Int, bytes: Bytes)
{
def id = bytes.arr(start)
def delete() = bytes.arr(start) = 3
override def toString = "SigEntry("+index+","+id+","+bytes.len+" bytes)"
}
trait Literal
{
def value:Any
}
case class BooleanLiteral(value:Boolean) extends Literal
case class ByteLiteral(value:Byte) extends Literal
case class ShortLiteral(value:Short) extends Literal
case class CharLiteral(value:Char) extends Literal
case class IntLiteral(value:Int) extends Literal
case class LongLiteral(value:Long) extends Literal
case class FloatLiteral(value:Float) extends Literal
case class DoubleLiteral(value:Double) extends Literal
case object NullLiteral extends Literal
{
override def value = null
}
case class StringLiteral(value:String) extends Literal
case class TypeLiteral(value:TypeRef) extends Literal
case class EnumLiteral(value:ExternalSymbol) extends Literal
case class ArrayLiteral(value:List[_]) extends Literal
case class AnnotationInfo(owner:SymbolRef, annType:TypeRef, values:Map[String, Literal])
{
def getValue[T](name:String) = values(name).asInstanceOf[T]
}
def evalS(i: Int): String = {
val e = table(i)
val bc = e.bytes
val bcr = bc.reader
e.id match {
case 1 | 2 => bcr.readString(bc.len)
case 3 => NoSymbol.full
case 9 | 10 =>
var s = evalS(bcr.readNat)
if (bc.pos + bc.len > bcr.pos)
s = evalS(bcr.readNat) + "." + s
s
}
}
def evalT[T](i: Int) = eval(i).asInstanceOf[T]
def evalList[T](bcr: ByteCodeReader) = {
val l = List.newBuilder
while (bcr.more)
l += evalT(bcr.readNat)
l.result()
}
def eval(i: Int): Any = {
//only parse the ones that matter for this project
val e = table(i)
val bcr = e.bytes.reader
def nat = bcr.readNat
def evalS = this.evalS(nat)
def evalT[T] = this.evalT[T](nat)
def evalList[T] = this.evalList[T](bcr)
e.id match {
case 1 | 2 => this.evalS(i)
case 3 => NoSymbol
case 6 => ClassSymbol(evalS, evalT, nat, nat)
case 7 => ObjectSymbol(evalS, evalT, nat, nat)
case 8 => MethodSymbol(evalS, evalT, nat, nat)
case 9 | 10 => ExternalSymbol(this.evalS(i))
case 11 | 12 => NoType //12 is actually NoPrefixType (no lower bound)
case 13 => ThisType(evalT)
case 14 => SingleType(evalT, evalT)
case 16 => TypeRefType(evalT, evalT, evalList)
case 19 => ClassType(evalT, evalList)
case 20 => MethodType(evalT, evalList)
case 21 | 48 => ParameterlessType(evalT) //48 is actually a bounded super type, but it should work fine for this project
case 25 => BooleanLiteral(bcr.readLong != 0)
case 26 => ByteLiteral(bcr.readLong.toByte)
case 27 => ShortLiteral(bcr.readLong.toShort)
case 28 => CharLiteral(bcr.readLong.toChar)
case 29 => IntLiteral(bcr.readLong.toInt)
case 30 => LongLiteral(bcr.readLong)
case 31 => FloatLiteral(java.lang.Float.intBitsToFloat(bcr.readLong.toInt))
case 32 => DoubleLiteral(java.lang.Double.longBitsToDouble(bcr.readLong))
case 33 => StringLiteral(evalS)
case 34 => NullLiteral
case 35 => TypeLiteral(evalT)
case 36 => EnumLiteral(evalT)
case 40 => AnnotationInfo(evalT, evalT, evalList.grouped(2).map(g => (g(0), g(1))).toMap)
case 44 => ArrayLiteral(evalList)
case _ => e
}
}
def collect[T](id: Int) = (0 until table.length).collect {
case i if table(i).id == id => evalT(i): T
}
def findObject(name: String) = collect[ObjectSymbol](7).find(_.full == name)
def findClass(name: String) = collect[ClassSymbol](6).find(c => !c.isModule && c.full == name)
def findObject(name: String) = collect[ObjectSymbol](7).find(_.full == name)
def findClass(name: String) =
collect[ClassSymbol](6).find(c => !c.isModule && c.full == name)
}
class ByteCodeReader(val bc: Bytes)
{
var pos = bc.pos
class ByteCodeReader(val bc: Bytes) {
var pos = bc.pos
def more = pos < bc.pos + bc.len
def more = pos < bc.pos + bc.len
def readString(len: Int) = advance(len)(new String(bc.arr.drop(pos).take(len)))
def readString(len: Int) =
advance(len)(new String(bc.arr.drop(pos).take(len)))
def readByte = advance(1)(bc.arr(pos))
def readByte = advance(1)(bc.arr(pos))
def readNat = {
var r = 0
var b = 0
do {
b = readByte
r = r << 7 | b & 0x7F
}
while ((b & 0x80) != 0)
r
def readNat = {
var r = 0
var b = 0
do {
b = readByte
r = r << 7 | b & 0x7f
} while ((b & 0x80) != 0)
r
}
def readLong = {
var l = 0L
while (more) {
l <<= 8
l |= readByte & 0xff
}
l
}
def readLong = {
var l = 0L
while(more) {
l <<= 8
l |= readByte & 0xFF
}
l
}
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
r
}
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
r
}
}
object ScalaSigReader
{
def decode(s: String) = {
val bytes = s.getBytes
bytes take ByteCodecs.decode(bytes)
object ScalaSigReader {
def decode(s: String) = {
val bytes = s.getBytes
bytes take ByteCodecs.decode(bytes)
}
def encode(b: Array[Byte]) = {
val bytes = ByteCodecs.encode8to7(b)
var i = 0
while (i < bytes.length) {
bytes(i) = ((bytes(i) + 1) & 0x7f).toByte
i += 1
}
new String(bytes.take(bytes.length - 1), "UTF-8")
}
def encode(b: Array[Byte]) = {
val bytes = ByteCodecs.encode8to7(b)
var i = 0
while (i < bytes.length) {
bytes(i) = ((bytes(i) + 1) & 0x7F).toByte
i += 1
}
new String(bytes.take(bytes.length - 1), "UTF-8")
def read(ann: AnnotationNode): ScalaSignature = new ScalaSignature(
Bytes(decode(ann.values.get(1).asInstanceOf[String]))
)
def write(sig: ScalaSignature, ann: AnnotationNode) =
ann.values.set(1, encode(sig.bytes.arr))
def ann(cnode: ClassNode): Option[AnnotationNode] =
cnode.visibleAnnotations match {
case null => None
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
}
def read(ann: AnnotationNode): ScalaSignature = new ScalaSignature(Bytes(decode(ann.values.get(1).asInstanceOf[String])))
def write(sig: ScalaSignature, ann: AnnotationNode) = ann.values.set(1, encode(sig.bytes.arr))
def ann(cnode: ClassNode): Option[AnnotationNode] = cnode.visibleAnnotations match {
case null => None
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
}
}
}
@@ -1,21 +1,20 @@
package codechicken.multipart.asm
import java.util.BitSet
trait ScratchBitSet
{
private val bitSets = new ThreadLocal[BitSet]
trait ScratchBitSet {
private val bitSets = new ThreadLocal[BitSet]
def getBitSet = bitSets.get match {
case null =>
val bitset = new BitSet
bitSets.set(bitset)
bitset
case bitset => bitset
}
def getBitSet = bitSets.get match {
case null =>
val bitset = new BitSet
bitSets.set(bitset)
bitset
case bitset => bitset
}
def freshBitSet = {
val bitset = getBitSet
bitset.clear()
bitset
}
def freshBitSet = {
val bitset = getBitSet
bitset.clear()
bitset
}
}
@@ -7,294 +7,300 @@ import org.objectweb.asm.Type
import org.objectweb.asm.Type._
import scala.collection.JavaConversions._
object StackAnalyser
{
def width(t:Type):Int = t.getSize
def width(s:String):Int = width(Type.getType(s))
def width(it:Iterable[Type]):Int = it.foldLeft(0)(_+width(_))
abstract class StackEntry(implicit val insn:AbstractInsnNode)
{
def getType:Type
}
abstract class LocalEntry
{
def getType:Type
}
object StackAnalyser {
def width(t: Type): Int = t.getSize
def width(s: String): Int = width(Type.getType(s))
def width(it: Iterable[Type]): Int = it.foldLeft(0)(_ + width(_))
case class This(owner:Type) extends LocalEntry
{
def getType = owner
}
case class Param(i:Int, t:Type) extends LocalEntry
{
def getType = t
}
case class Store(e:StackEntry)(implicit val insn:AbstractInsnNode) extends LocalEntry
{
def getType = e.getType
}
abstract class StackEntry(implicit val insn: AbstractInsnNode) {
def getType: Type
}
abstract class LocalEntry {
def getType: Type
}
case class Const(c:Any)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = c match {
case o:Byte => BYTE_TYPE
case o:Short => SHORT_TYPE
case o:Int => INT_TYPE
case o:Long => LONG_TYPE
case o:Float => FLOAT_TYPE
case o:Double => DOUBLE_TYPE
case o:Char => CHAR_TYPE
case o:Boolean => BOOLEAN_TYPE
case o:String => getObjectType("java/lang/String")
case null => getObjectType("java/lang/Object")
case _ => throw new IllegalArgumentException("Unknown const "+c)
}
}
case class Load(e:LocalEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType
}
case class UnaryOp(op:Int, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType
}
case class BinaryOp(op:Int, e2:StackEntry, e1:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e1.getType
}
case class PrimitiveCast(e:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class ReturnAddress()(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = INT_TYPE
}
case class GetField(obj:StackEntry, field:FieldInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = Type.getType(field.desc)
}
case class Invoke(op:Int, params:Array[StackEntry], obj:StackEntry, method:MethodInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = Type.getMethodType(method.desc).getReturnType
}
case class New(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class NewArray(len:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class ArrayLength(array:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = INT_TYPE
}
case class ArrayLoad(index:StackEntry, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType.getElementType
}
case class Cast(obj:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class NewMultiArray(sizes:Array[StackEntry], t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class CaughtException(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
case class This(owner: Type) extends LocalEntry {
def getType = owner
}
case class Param(i: Int, t: Type) extends LocalEntry {
def getType = t
}
case class Store(e: StackEntry)(implicit val insn: AbstractInsnNode)
extends LocalEntry {
def getType = e.getType
}
case class Const(c: Any)(implicit insn: AbstractInsnNode) extends StackEntry {
def getType = c match {
case o: Byte => BYTE_TYPE
case o: Short => SHORT_TYPE
case o: Int => INT_TYPE
case o: Long => LONG_TYPE
case o: Float => FLOAT_TYPE
case o: Double => DOUBLE_TYPE
case o: Char => CHAR_TYPE
case o: Boolean => BOOLEAN_TYPE
case o: String => getObjectType("java/lang/String")
case null => getObjectType("java/lang/Object")
case _ => throw new IllegalArgumentException("Unknown const " + c)
}
}
case class Load(e: LocalEntry)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = e.getType
}
case class UnaryOp(op: Int, e: StackEntry)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = e.getType
}
case class BinaryOp(op: Int, e2: StackEntry, e1: StackEntry)(implicit
insn: AbstractInsnNode
) extends StackEntry {
def getType = e1.getType
}
case class PrimitiveCast(e: StackEntry, t: Type)(implicit
insn: AbstractInsnNode
) extends StackEntry {
def getType = t
}
case class ReturnAddress()(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = INT_TYPE
}
case class GetField(obj: StackEntry, field: FieldInsnNode)(implicit
insn: AbstractInsnNode
) extends StackEntry {
def getType = Type.getType(field.desc)
}
case class Invoke(
op: Int,
params: Array[StackEntry],
obj: StackEntry,
method: MethodInsnNode
)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = Type.getMethodType(method.desc).getReturnType
}
case class New(t: Type)(implicit insn: AbstractInsnNode) extends StackEntry {
def getType = t
}
case class NewArray(len: StackEntry, t: Type)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = t
}
case class ArrayLength(array: StackEntry)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = INT_TYPE
}
case class ArrayLoad(index: StackEntry, e: StackEntry)(implicit
insn: AbstractInsnNode
) extends StackEntry {
def getType = e.getType.getElementType
}
case class Cast(obj: StackEntry, t: Type)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = t
}
case class NewMultiArray(sizes: Array[StackEntry], t: Type)(implicit
insn: AbstractInsnNode
) extends StackEntry {
def getType = t
}
case class CaughtException(t: Type)(implicit insn: AbstractInsnNode)
extends StackEntry {
def getType = t
}
}
class StackAnalyser(val owner:Type, val m:MethodNode)
{
import StackAnalyser._
val stack = MList[StackEntry]()
val locals = MList[LocalEntry]()
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
{
if((m.access & ACC_STATIC) == 0)
pushL(This(owner))
val ptypes = getArgumentTypes(m.desc)
for(i <- 0 until ptypes.length)
pushL(Param(i, ptypes(i)))
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
class StackAnalyser(val owner: Type, val m: MethodNode) {
import StackAnalyser._
val stack = MList[StackEntry]()
val locals = MList[LocalEntry]()
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
{
if ((m.access & ACC_STATIC) == 0)
pushL(This(owner))
val ptypes = getArgumentTypes(m.desc)
for (i <- 0 until ptypes.length)
pushL(Param(i, ptypes(i)))
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
}
def pushL(entry: LocalEntry) = setL(locals.size, entry)
def setL(i: Int, entry: LocalEntry) = {
while (i + entry.getType.getSize > locals.size) locals += null
locals(i) = entry
if (entry.getType.getSize == 2)
locals(i + 1) = entry
}
def push(entry: StackEntry) = insert(0, entry)
def _pop(i: Int = 0) = stack.remove(stack.size - i - 1)
def pop(i: Int = 0) = {
val e = _pop(i)
if (e.getType.getSize == 2) {
if (peek(i) != e)
throw new IllegalStateException(
"Wide stack entry elems don't match (" + e + "," + peek(i)
)
_pop(i)
}
def pushL(entry:LocalEntry) = setL(locals.size, entry)
e
}
def setL(i:Int, entry:LocalEntry) =
{
while(i+entry.getType.getSize > locals.size) locals += null
locals(i) = entry
if(entry.getType.getSize == 2)
locals(i+1) = entry
}
def push(entry:StackEntry) = insert(0, entry)
def peek(i: Int = 0) = stack(stack.size - i - 1)
def _pop(i:Int = 0) = stack.remove(stack.size-i-1)
def insert(i: Int, entry: StackEntry) {
if (entry.getType.getSize == 0)
return
stack.insert(stack.size - i, entry)
if (entry.getType.getSize == 2)
stack.insert(stack.size - i, entry)
}
def popArgs(desc: String) = {
val t = getType(desc)
val args = new Array[StackEntry](t.getArgumentTypes.length)
for (i <- 0 until args.length)
args(args.length - i - 1) = pop()
args
}
def visitInsn(ainsn: AbstractInsnNode) {
implicit val thisInsn = ainsn // passes to any StackEntry we create
ainsn match {
case insn: InsnNode =>
ainsn.getOpcode match {
case ACONST_NULL => push(Const(null))
case ICONST_M1 => push(Const(-1))
case ICONST_0 => push(Const(0))
case ICONST_1 => push(Const(1))
case ICONST_2 => push(Const(2))
case ICONST_3 => push(Const(3))
case ICONST_4 => push(Const(4))
case ICONST_5 => push(Const(5))
case LCONST_0 => push(Const(0L))
case LCONST_1 => push(Const(1L))
case FCONST_0 => push(Const(0f))
case FCONST_1 => push(Const(1f))
case FCONST_2 => push(Const(2f))
case DCONST_0 => push(Const(0d))
case DCONST_1 => push(Const(1d))
case i if i >= IALOAD && i <= SALOAD =>
push(ArrayLoad(pop(), pop()))
case i if i >= IASTORE && i <= SASTORE =>
pop(); pop(); pop()
case POP => _pop()
case POP2 => _pop(); _pop()
case DUP => push(peek())
case DUP_X1 => insert(2, peek())
case DUP_X2 => insert(3, peek())
case DUP2 => push(peek(1)); push(peek(1))
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
case SWAP => push(pop(1))
case i if i >= IADD && i <= DREM =>
push(BinaryOp(i, pop(), pop()))
case i if i >= INEG && i <= DNEG =>
push(UnaryOp(i, pop()))
case i if i >= ISHL && i <= LXOR =>
push(BinaryOp(i, pop(), pop()))
case L2I | F2I | D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2L | F2L | D2L => push(PrimitiveCast(pop(), LONG_TYPE))
case I2F | L2F | D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
case I2D | L2D | F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
case i if i >= LCMP && i <= DCMPG =>
push(BinaryOp(i, pop(), pop()))
case i if i >= IRETURN && i <= ARETURN => pop()
case ARRAYLENGTH => push(ArrayLength(pop()))
case ATHROW => pop()
case MONITORENTER | MONITOREXIT => pop()
case _ =>
def pop(i:Int = 0) =
{
val e = _pop(i)
if(e.getType.getSize == 2)
{
if(peek(i) != e) throw new IllegalStateException("Wide stack entry elems don't match ("+e+","+peek(i))
_pop(i)
}
e
}
def peek(i:Int = 0) = stack(stack.size-i-1)
def insert(i:Int, entry:StackEntry)
{
if(entry.getType.getSize == 0)
return
stack.insert(stack.size-i, entry)
if(entry.getType.getSize == 2)
stack.insert(stack.size-i, entry)
}
def popArgs(desc:String) =
{
val t = getType(desc)
val args = new Array[StackEntry](t.getArgumentTypes.length)
for(i <- 0 until args.length)
args(args.length-i-1) = pop()
args
}
def visitInsn(ainsn:AbstractInsnNode)
{
implicit val thisInsn = ainsn//passes to any StackEntry we create
ainsn match {
case insn:InsnNode => ainsn.getOpcode match
{
case ACONST_NULL => push(Const(null))
case ICONST_M1 => push(Const(-1))
case ICONST_0 => push(Const(0))
case ICONST_1 => push(Const(1))
case ICONST_2 => push(Const(2))
case ICONST_3 => push(Const(3))
case ICONST_4 => push(Const(4))
case ICONST_5 => push(Const(5))
case LCONST_0 => push(Const(0L))
case LCONST_1 => push(Const(1L))
case FCONST_0 => push(Const(0F))
case FCONST_1 => push(Const(1F))
case FCONST_2 => push(Const(2F))
case DCONST_0 => push(Const(0D))
case DCONST_1 => push(Const(1D))
case i if i >= IALOAD && i <= SALOAD =>
push(ArrayLoad(pop(), pop()))
case i if i >= IASTORE && i <= SASTORE =>
pop(); pop(); pop()
case POP => _pop()
case POP2 => _pop(); _pop()
case DUP => push(peek())
case DUP_X1 => insert(2, peek())
case DUP_X2 => insert(3, peek())
case DUP2 => push(peek(1)); push(peek(1))
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
case SWAP => push(pop(1))
case i if i >= IADD && i <= DREM =>
push(BinaryOp(i, pop(), pop()))
case i if i >= INEG && i <= DNEG =>
push(UnaryOp(i, pop()))
case i if i >= ISHL && i <= LXOR =>
push(BinaryOp(i, pop(), pop()))
case L2I|F2I|D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2L|F2L|D2L => push(PrimitiveCast(pop(), LONG_TYPE))
case I2F|L2F|D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
case I2D|L2D|F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
case i if i >= LCMP && i <= DCMPG =>
push(BinaryOp(i, pop(), pop()))
case i if i >= IRETURN && i <= ARETURN => pop()
case ARRAYLENGTH => push(ArrayLength(pop()))
case ATHROW => pop()
case MONITORENTER|MONITOREXIT => pop()
case _ =>
}
case iinsn:IntInsnNode => ainsn.getOpcode match
{
case BIPUSH => push(Const(iinsn.operand.toByte))
case SIPUSH => push(Const(iinsn.operand.toShort))
}
case ldcinsn:LdcInsnNode => ainsn.getOpcode match
{
case LDC => push(Const(ldcinsn.cst))
}
case vinsn:VarInsnNode => ainsn.getOpcode match
{
case i if i >= ILOAD && i <= ALOAD =>
push(Load(locals(vinsn.`var`)))
case i if i >= ISTORE && i <= ASTORE =>
setL(vinsn.`var`, Store(pop()))
}
case incinsn:IincInsnNode => ainsn.getOpcode match
{
case IINC => setL(incinsn.`var`, Store(BinaryOp(IINC, Const(incinsn.incr), Load(locals(incinsn.`var`)))))
}
case jinsn:JumpInsnNode => ainsn.getOpcode match
{
case i if i >= IFEQ && i <= IFLE => pop()
case i if i >= IF_ICMPEQ && i <= IF_ACMPNE => pop(); pop()
case JSR => push(ReturnAddress())
case IFNULL|IFNONNULL => pop()
case GOTO =>
}
case sinsn:TableSwitchInsnNode => pop()
case linsn:LookupSwitchInsnNode => pop()
case finsn:FieldInsnNode => ainsn.getOpcode match
{
case GETSTATIC => push(GetField(null, finsn))
case PUTSTATIC => pop()
case GETFIELD => push(GetField(pop(), finsn))
case PUTFIELD => pop(); pop()
}
case minsn:MethodInsnNode => ainsn.getOpcode match
{
case INVOKEVIRTUAL|INVOKESPECIAL|INVOKEINTERFACE =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
case INVOKESTATIC =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
}
case tinsn:TypeInsnNode => ainsn.getOpcode match
{
case NEW => push(New(getObjectType(tinsn.desc)))
case NEWARRAY => push(NewArray(pop(), getObjectType(tinsn.desc)))
case ANEWARRAY => push(NewArray(pop(), getObjectType("["+tinsn.desc)))
case CHECKCAST => push(Cast(pop(), getObjectType(tinsn.desc)))
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
}
case mainsn:MultiANewArrayInsnNode =>
val sizes = new Array[StackEntry](mainsn.dims)
for(i <- 0 until sizes.length)
sizes(i) = pop()
push(NewMultiArray(sizes, getType(mainsn.desc)))
/*case fnode:FrameNode => fnode.`type` match
case iinsn: IntInsnNode =>
ainsn.getOpcode match {
case BIPUSH => push(Const(iinsn.operand.toByte))
case SIPUSH => push(Const(iinsn.operand.toShort))
}
case ldcinsn: LdcInsnNode =>
ainsn.getOpcode match {
case LDC => push(Const(ldcinsn.cst))
}
case vinsn: VarInsnNode =>
ainsn.getOpcode match {
case i if i >= ILOAD && i <= ALOAD =>
push(Load(locals(vinsn.`var`)))
case i if i >= ISTORE && i <= ASTORE =>
setL(vinsn.`var`, Store(pop()))
}
case incinsn: IincInsnNode =>
ainsn.getOpcode match {
case IINC =>
setL(
incinsn.`var`,
Store(
BinaryOp(IINC, Const(incinsn.incr), Load(locals(incinsn.`var`)))
)
)
}
case jinsn: JumpInsnNode =>
ainsn.getOpcode match {
case i if i >= IFEQ && i <= IFLE => pop()
case i if i >= IF_ICMPEQ && i <= IF_ACMPNE => pop(); pop()
case JSR => push(ReturnAddress())
case IFNULL | IFNONNULL => pop()
case GOTO =>
}
case sinsn: TableSwitchInsnNode => pop()
case linsn: LookupSwitchInsnNode => pop()
case finsn: FieldInsnNode =>
ainsn.getOpcode match {
case GETSTATIC => push(GetField(null, finsn))
case PUTSTATIC => pop()
case GETFIELD => push(GetField(pop(), finsn))
case PUTFIELD => pop(); pop()
}
case minsn: MethodInsnNode =>
ainsn.getOpcode match {
case INVOKEVIRTUAL | INVOKESPECIAL | INVOKEINTERFACE =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
case INVOKESTATIC =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
}
case tinsn: TypeInsnNode =>
ainsn.getOpcode match {
case NEW => push(New(getObjectType(tinsn.desc)))
case NEWARRAY => push(NewArray(pop(), getObjectType(tinsn.desc)))
case ANEWARRAY =>
push(NewArray(pop(), getObjectType("[" + tinsn.desc)))
case CHECKCAST => push(Cast(pop(), getObjectType(tinsn.desc)))
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
}
case mainsn: MultiANewArrayInsnNode =>
val sizes = new Array[StackEntry](mainsn.dims)
for (i <- 0 until sizes.length)
sizes(i) = pop()
push(NewMultiArray(sizes, getType(mainsn.desc)))
/*case fnode:FrameNode => fnode.`type` match
{
case F_NEW|F_FULL => println("reset stacks/locals")
case F_APPEND => println("add local")
@@ -302,13 +308,13 @@ class StackAnalyser(val owner:Type, val m:MethodNode)
case F_SAME => println("reset")
case F_SAME1 => println("reset locals and all but bottom stack")
}*/
case lnode:LabelNode =>
catchHandlers.get(lnode) match
{
case Some(tcblock) => push(CaughtException(Type.getType(tcblock.`type`)))
case None =>
}
case _ =>
case lnode: LabelNode =>
catchHandlers.get(lnode) match {
case Some(tcblock) =>
push(CaughtException(Type.getType(tcblock.`type`)))
case None =>
}
case _ =>
}
}
}
}
@@ -4,28 +4,44 @@ import net.minecraft.world.World
import cpw.mods.fml.common.FMLCommonHandler
import net.minecraft.block.Block
object MultipartCompatiblity
{
var canAddPart = (world:World, x:Int, y:Int, z:Int) => true
object MultipartCompatiblity {
var canAddPart = (world: World, x: Int, y: Int, z: Int) => true
def load() {
if(FMLCommonHandler.instance().getModName.contains("mcpc"))
MCPCCompatModule.load()
}
def load() {
if (FMLCommonHandler.instance().getModName.contains("mcpc"))
MCPCCompatModule.load()
}
}
object MCPCCompatModule
{
def load() {
try {
val m_canPlacePart = classOf[World].getDeclaredMethod("canPlaceMultipart", classOf[Block], classOf[Int], classOf[Int], classOf[Int])
MultipartCompatiblity.canAddPart = (world:World, x:Int, y:Int, z:Int) => {
m_canPlacePart.invoke(world, MultipartProxy.block, x:Integer, y:Integer, z:Integer).asInstanceOf[Boolean].booleanValue()
}
} catch {
case e:Exception => MultipartProxy.logger.error("Failed to integrate MCPC placement hooks", e)
object MCPCCompatModule {
def load() {
try {
val m_canPlacePart = classOf[World].getDeclaredMethod(
"canPlaceMultipart",
classOf[Block],
classOf[Int],
classOf[Int],
classOf[Int]
)
MultipartCompatiblity.canAddPart =
(world: World, x: Int, y: Int, z: Int) => {
m_canPlacePart
.invoke(
world,
MultipartProxy.block,
x: Integer,
y: Integer,
z: Integer
)
.asInstanceOf[Boolean]
.booleanValue()
}
} catch {
case e: Exception =>
MultipartProxy.logger.error(
"Failed to integrate MCPC placement hooks",
e
)
}
}
}
@@ -1,6 +1,11 @@
package codechicken.multipart.handler
import codechicken.multipart.{TileCache, TileMultipart, MultiPartRegistry, BlockMultipart}
import codechicken.multipart.{
TileCache,
TileMultipart,
MultiPartRegistry,
BlockMultipart
}
import cpw.mods.fml.common.eventhandler.{EventPriority, SubscribeEvent}
import net.minecraft.server.MinecraftServer
import codechicken.lib.packet.PacketCustom
@@ -16,52 +21,61 @@ import net.minecraft.util.MovingObjectPosition
import net.minecraft.util.MovingObjectPosition.MovingObjectType
import cpw.mods.fml.common.gameevent.TickEvent
object MultipartEventHandler
{
@SubscribeEvent(priority = EventPriority.HIGHEST)
def tileEntityLoad(event:ChunkDataEvent.Load)
{
MultipartSaveLoad.loadTiles(event.getChunk)
}
@SubscribeEvent
def worldUnLoad(event:WorldEvent.Unload)
{
MultipartSPH.onWorldUnload(event.world)
if(event.world.isRemote)
TileCache.clear()
}
object MultipartEventHandler {
@SubscribeEvent(priority = EventPriority.HIGHEST)
def tileEntityLoad(event: ChunkDataEvent.Load) {
MultipartSaveLoad.loadTiles(event.getChunk)
}
@SubscribeEvent
def chunkWatch(event:ChunkWatchEvent.Watch)
{
MultipartSPH.onChunkWatch(event.player, event.chunk)
}
@SubscribeEvent
def worldUnLoad(event: WorldEvent.Unload) {
MultipartSPH.onWorldUnload(event.world)
if (event.world.isRemote)
TileCache.clear()
}
@SubscribeEvent
def chunkUnWatch(event:ChunkWatchEvent.UnWatch)
{
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event:DrawBlockHighlightEvent)
{
if(event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
event.player.worldObj.getTileEntity(event.target.blockX, event.target.blockY, event.target.blockZ).isInstanceOf[TileMultipart])
{
if(BlockMultipart.drawHighlight(event.player.worldObj, event.player, event.target, event.partialTicks))
event.setCanceled(true)
}
}
@SubscribeEvent
def chunkWatch(event: ChunkWatchEvent.Watch) {
MultipartSPH.onChunkWatch(event.player, event.chunk)
}
@SubscribeEvent
def serverTick(event:TickEvent.ServerTickEvent)
{
if(event.phase == TickEvent.Phase.END)
MultipartSPH.onTickEnd(
MinecraftServer.getServer.getConfigurationManager.playerEntityList
.asInstanceOf[List[EntityPlayerMP]].asScala)
@SubscribeEvent
def chunkUnWatch(event: ChunkWatchEvent.UnWatch) {
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event: DrawBlockHighlightEvent) {
if (
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
event.player.worldObj
.getTileEntity(
event.target.blockX,
event.target.blockY,
event.target.blockZ
)
.isInstanceOf[TileMultipart]
) {
if (
BlockMultipart.drawHighlight(
event.player.worldObj,
event.player,
event.target,
event.partialTicks
)
)
event.setCanceled(true)
}
}
}
@SubscribeEvent
def serverTick(event: TickEvent.ServerTickEvent) {
if (event.phase == TickEvent.Phase.END)
MultipartSPH.onTickEnd(
MinecraftServer.getServer.getConfigurationManager.playerEntityList
.asInstanceOf[List[EntityPlayerMP]]
.asScala
)
}
}
@@ -8,29 +8,32 @@ import cpw.mods.fml.common.Mod.EventHandler
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent
import codechicken.multipart.MultiPartRegistry
@Mod(modid = "ForgeMultipart", acceptedMinecraftVersions = "[1.7.10]", modLanguage = "scala")
object MultipartMod
{
@EventHandler
def preInit(event: FMLPreInitializationEvent) {
MultipartProxy.preInit(event.getModConfigurationDirectory, event.getModLog)
}
@Mod(
modid = "ForgeMultipart",
acceptedMinecraftVersions = "[1.7.10]",
modLanguage = "scala"
)
object MultipartMod {
@EventHandler
def preInit(event: FMLPreInitializationEvent) {
MultipartProxy.preInit(event.getModConfigurationDirectory, event.getModLog)
}
@EventHandler
def init(event: FMLInitializationEvent) {
MultipartProxy.init()
}
@EventHandler
def init(event: FMLInitializationEvent) {
MultipartProxy.init()
}
@EventHandler
def postInit(event: FMLPostInitializationEvent) {
if (MultiPartRegistry.required) {
MultiPartRegistry.postInit()
MultipartProxy.postInit()
}
@EventHandler
def postInit(event: FMLPostInitializationEvent) {
if (MultiPartRegistry.required) {
MultiPartRegistry.postInit()
MultipartProxy.postInit()
}
}
@EventHandler
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MultiPartRegistry.beforeServerStart()
}
}
@EventHandler
def beforeServerStart(event: FMLServerAboutToStartEvent) {
MultiPartRegistry.beforeServerStart()
}
}
@@ -12,78 +12,88 @@ import scala.collection.mutable
import codechicken.multipart.MultipartHelper.IPartTileConverter
import scala.collection.JavaConversions._
/**
* Hack due to lack of TileEntityLoadEvent in forge
*/
object MultipartSaveLoad
{
val converters = mutable.MutableList[IPartTileConverter[_]]()
var loadingWorld: World = _
/** Hack due to lack of TileEntityLoadEvent in forge
*/
object MultipartSaveLoad {
val converters = mutable.MutableList[IPartTileConverter[_]]()
var loadingWorld: World = _
class TileNBTContainer extends TileEntity
{
var tag: NBTTagCompound = _
class TileNBTContainer extends TileEntity {
var tag: NBTTagCompound = _
override def readFromNBT(t: NBTTagCompound) {
super.readFromNBT(t)
tag = t
}
override def readFromNBT(t: NBTTagCompound) {
super.readFromNBT(t)
tag = t
}
}
def hookLoader() {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145855_i", "Ljava/util/Map;")
.toRuntime.s_name)
field.setAccessible(true)
val map = field.get(null).asInstanceOf[Map[String, Class[_ <: TileEntity]]]
map.put("savedMultipart", classOf[TileNBTContainer])
def hookLoader() {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping(
"net/minecraft/tileentity/TileEntity",
"field_145855_i",
"Ljava/util/Map;"
).toRuntime.s_name
)
field.setAccessible(true)
val map = field.get(null).asInstanceOf[Map[String, Class[_ <: TileEntity]]]
map.put("savedMultipart", classOf[TileNBTContainer])
}
private val classToNameMap = getClassToNameMap
def registerTileClass(t: Class[_ <: TileEntity]) {
classToNameMap.put(t, "savedMultipart")
}
def getClassToNameMap = {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping(
"net/minecraft/tileentity/TileEntity",
"field_145853_j",
"Ljava/util/Map;"
).toRuntime.s_name
)
field.setAccessible(true)
field.get(null).asInstanceOf[Map[Class[_ <: TileEntity], String]]
}
def loadTiles(chunk: Chunk) {
loadingWorld = chunk.worldObj
val iterator = chunk.chunkTileEntityMap
.asInstanceOf[Map[ChunkPosition, TileEntity]]
.entrySet
.iterator
while (iterator.hasNext) {
val e = iterator.next
var next = false
val t = e.getValue match {
case t: TileNBTContainer if t.tag.getString("id") == "savedMultipart" =>
TileMultipart.createFromNBT(
e.getValue.asInstanceOf[TileNBTContainer].tag
)
case t =>
converters.find(_.canConvert(t)) match {
case Some(c) =>
val parts = c.convert(t)
if (!parts.isEmpty)
MultipartHelper.createTileFromParts(parts)
else
null
case _ =>
next = true
null
}
}
if (!next) {
if (t != null) {
t.setWorldObj(e.getValue.getWorldObj)
t.validate()
e.setValue(t)
} else
iterator.remove()
}
}
private val classToNameMap = getClassToNameMap
def registerTileClass(t: Class[_ <: TileEntity]) {
classToNameMap.put(t, "savedMultipart")
}
def getClassToNameMap = {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145853_j", "Ljava/util/Map;")
.toRuntime.s_name)
field.setAccessible(true)
field.get(null).asInstanceOf[Map[Class[_ <: TileEntity], String]]
}
def loadTiles(chunk: Chunk) {
loadingWorld = chunk.worldObj
val iterator = chunk.chunkTileEntityMap.asInstanceOf[Map[ChunkPosition, TileEntity]].entrySet.iterator
while (iterator.hasNext) {
val e = iterator.next
var next = false
val t = e.getValue match {
case t:TileNBTContainer if t.tag.getString("id") == "savedMultipart" =>
TileMultipart.createFromNBT(e.getValue.asInstanceOf[TileNBTContainer].tag)
case t => converters.find(_.canConvert(t)) match {
case Some(c) =>
val parts = c.convert(t)
if(!parts.isEmpty)
MultipartHelper.createTileFromParts(parts)
else
null
case _ =>
next = true
null
}
}
if(!next) {
if (t != null) {
t.setWorldObj(e.getValue.getWorldObj)
t.validate()
e.setValue(t)
}
else
iterator.remove()
}
}
}
}
}
}
@@ -1,6 +1,10 @@
package codechicken.multipart.handler
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
import codechicken.lib.packet.PacketCustom.{
IHandshakeHandler,
IClientPacketHandler,
IServerPacketHandler
}
import codechicken.lib.packet.PacketCustom
import net.minecraft.client.Minecraft
import codechicken.multipart.MultiPartRegistry
@@ -28,160 +32,207 @@ import net.minecraft.network.play.server.S40PacketDisconnect
import net.minecraft.util.{ChatComponentText, ChatComponentTranslation}
import net.minecraft.network.NetHandlerPlayServer
class MultipartPH
{
val channel = MultipartMod
val registryChannel = "ForgeMultipart"
class MultipartPH {
val channel = MultipartMod
val registryChannel = "ForgeMultipart"
}
object MultipartCPH extends MultipartPH with IClientPacketHandler
{
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
try {
packet.getType match {
case 1 => handlePartRegistration(packet, netHandler)
case 2 => handleCompressedTileDesc(packet, mc.theWorld)
case 3 => handleCompressedTileData(packet, mc.theWorld)
}
}
catch {
case e:RuntimeException if e.getMessage != null && e.getMessage.startsWith("DC: ") =>
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentText(e.getMessage.substring(4))))
}
object MultipartCPH extends MultipartPH with IClientPacketHandler {
def handlePacket(
packet: PacketCustom,
mc: Minecraft,
netHandler: INetHandlerPlayClient
) {
try {
packet.getType match {
case 1 => handlePartRegistration(packet, netHandler)
case 2 => handleCompressedTileDesc(packet, mc.theWorld)
case 3 => handleCompressedTileData(packet, mc.theWorld)
}
} catch {
case e: RuntimeException
if e.getMessage != null && e.getMessage.startsWith("DC: ") =>
netHandler.handleDisconnect(
new S40PacketDisconnect(
new ChatComponentText(e.getMessage.substring(4))
)
)
}
}
def handlePartRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
val missing = MultiPartRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("multipart.missing", missing.mkString(", "))))
}
def handlePartRegistration(
packet: PacketCustom,
netHandler: INetHandlerPlayClient
) {
val missing = MultiPartRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(
new S40PacketDisconnect(
new ChatComponentTranslation(
"multipart.missing",
missing.mkString(", ")
)
)
)
}
def handleCompressedTileDesc(packet: PacketCustom, world: World) {
val cc = new ChunkCoordIntPair(packet.readInt, packet.readInt)
val num = packet.readUShort
for (i <- 0 until num)
TileMultipart.handleDescPacket(world, indexInChunk(cc, packet.readShort), packet)
}
def handleCompressedTileDesc(packet: PacketCustom, world: World) {
val cc = new ChunkCoordIntPair(packet.readInt, packet.readInt)
val num = packet.readUShort
for (i <- 0 until num)
TileMultipart.handleDescPacket(
world,
indexInChunk(cc, packet.readShort),
packet
)
}
def handleCompressedTileData(packet: PacketCustom, world: World) {
var x = packet.readInt
while (x != Int.MaxValue) {
val pos = new BlockCoord(x, packet.readInt, packet.readInt)
var i = packet.readUByte
while (i < 255) {
TileMultipart.handlePacket(pos, world, i, packet)
i = packet.readUByte
}
x = packet.readInt
}
def handleCompressedTileData(packet: PacketCustom, world: World) {
var x = packet.readInt
while (x != Int.MaxValue) {
val pos = new BlockCoord(x, packet.readInt, packet.readInt)
var i = packet.readUByte
while (i < 255) {
TileMultipart.handlePacket(pos, world, i, packet)
i = packet.readUByte
}
x = packet.readInt
}
}
}
object MultipartSPH extends MultipartPH with IServerPacketHandler with IHandshakeHandler
{
class MCByteStream(bout: ByteArrayOutputStream) extends MCDataOutputWrapper(new DataOutputStream(bout))
{
def getBytes = bout.toByteArray
object MultipartSPH
extends MultipartPH
with IServerPacketHandler
with IHandshakeHandler {
class MCByteStream(bout: ByteArrayOutputStream)
extends MCDataOutputWrapper(new DataOutputStream(bout)) {
def getBytes = bout.toByteArray
}
private val updateMap = Map[World, Map[BlockCoord, MCByteStream]]()
/** These maps are keyed by entityID so that new player instances with the
* same entity id don't conflict world references
*/
private val chunkWatchers = new HashMap[Int, Set[ChunkCoordIntPair]]
with MultiMap[Int, ChunkCoordIntPair]
private val newWatchers = Map[Int, LinkedList[ChunkCoordIntPair]]()
def handlePacket(
packet: PacketCustom,
sender: EntityPlayerMP,
netHandler: INetHandlerPlayServer
) {
packet.getType match {
case 1 => ControlKeyModifer.map.put(sender, packet.readBoolean)
}
}
private val updateMap = Map[World, Map[BlockCoord, MCByteStream]]()
/**
* These maps are keyed by entityID so that new player instances with the same entity id don't conflict world references
*/
private val chunkWatchers = new HashMap[Int, Set[ChunkCoordIntPair]] with MultiMap[Int, ChunkCoordIntPair]
private val newWatchers = Map[Int, LinkedList[ChunkCoordIntPair]]()
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MultiPartRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {
packet.getType match {
case 1 => ControlKeyModifer.map.put(sender, packet.readBoolean)
def onWorldUnload(world: World) {
if (!world.isRemote)
updateMap.remove(world)
}
def getTileStream(world: World, pos: BlockCoord) =
updateMap
.getOrElseUpdate(
world, {
if (world.isRemote)
throw new IllegalArgumentException(
"Cannot use MultipartSPH on a client world"
)
Map()
}
}
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MultiPartRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
def onWorldUnload(world: World) {
if (!world.isRemote)
updateMap.remove(world)
}
def getTileStream(world: World, pos: BlockCoord) =
updateMap.getOrElseUpdate(world, {
if (world.isRemote)
throw new IllegalArgumentException("Cannot use MultipartSPH on a client world")
Map()
}).getOrElseUpdate(pos, {
val s = new MCByteStream(new ByteArrayOutputStream)
s.writeCoord(pos)
s
})
def onTickEnd(players: Seq[EntityPlayerMP]) {
PacketScheduler.sendScheduled()
for (p <- players if chunkWatchers.containsKey(p.getEntityId)) {
updateMap.get(p.worldObj) match {
case Some(m) if !m.isEmpty =>
val chunks = chunkWatchers(p.getEntityId)
val packet = new PacketCustom(channel, 3).compress()
var send = false
for ((pos, stream) <- m if chunks(new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4))) {
send = true
packet.writeByteArray(stream.getBytes)
packet.writeByte(255) //terminator
}
if (send) {
packet.writeInt(Int.MaxValue) //terminator
packet.sendToPlayer(p)
}
case _ =>
}
)
.getOrElseUpdate(
pos, {
val s = new MCByteStream(new ByteArrayOutputStream)
s.writeCoord(pos)
s
}
updateMap.foreach(_._2.clear())
for (p <- players if newWatchers.containsKey(p.getEntityId)) {
for (c <- newWatchers(p.getEntityId)) {
val chunk = p.worldObj.getChunkFromChunkCoords(c.chunkXPos, c.chunkZPos)
val pkt = getDescPacket(chunk, chunk.chunkTileEntityMap.asInstanceOf[JMap[_, TileEntity]].values.iterator)
if (pkt != null) pkt.sendToPlayer(p)
chunkWatchers.addBinding(p.getEntityId, c)
}
}
newWatchers.clear()
}
)
def onChunkWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.getOrElseUpdate(p.getEntityId, new LinkedList).add(c)
}
def onTickEnd(players: Seq[EntityPlayerMP]) {
PacketScheduler.sendScheduled()
def onChunkUnWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.get(p.getEntityId) match {
case Some(chunks) => chunks.remove(c)
case _ =>
}
chunkWatchers.removeBinding(p.getEntityId, c)
for (p <- players if chunkWatchers.containsKey(p.getEntityId)) {
updateMap.get(p.worldObj) match {
case Some(m) if !m.isEmpty =>
val chunks = chunkWatchers(p.getEntityId)
val packet = new PacketCustom(channel, 3).compress()
var send = false
for (
(pos, stream) <- m
if chunks(new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4))
) {
send = true
packet.writeByteArray(stream.getBytes)
packet.writeByte(255) // terminator
}
if (send) {
packet.writeInt(Int.MaxValue) // terminator
packet.sendToPlayer(p)
}
case _ =>
}
}
def getDescPacket(chunk: Chunk, it: Iterator[TileEntity]): PacketCustom = {
val s = new MCByteStream(new ByteArrayOutputStream)
var num = 0
while (it.hasNext) {
val tile = it.next
if (tile.isInstanceOf[TileMultipart]) {
s.writeShort(indexInChunk(new BlockCoord(tile)))
tile.asInstanceOf[TileMultipart].writeDesc(s)
num += 1
}
}
if (num != 0) {
return new PacketCustom(channel, 2).compress()
.writeInt(chunk.xPosition).writeInt(chunk.zPosition)
.writeShort(num)
.writeByteArray(s.getBytes)
}
return null
updateMap.foreach(_._2.clear())
for (p <- players if newWatchers.containsKey(p.getEntityId)) {
for (c <- newWatchers(p.getEntityId)) {
val chunk = p.worldObj.getChunkFromChunkCoords(c.chunkXPos, c.chunkZPos)
val pkt = getDescPacket(
chunk,
chunk.chunkTileEntityMap
.asInstanceOf[JMap[_, TileEntity]]
.values
.iterator
)
if (pkt != null) pkt.sendToPlayer(p)
chunkWatchers.addBinding(p.getEntityId, c)
}
}
newWatchers.clear()
}
def onChunkWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.getOrElseUpdate(p.getEntityId, new LinkedList).add(c)
}
def onChunkUnWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.get(p.getEntityId) match {
case Some(chunks) => chunks.remove(c)
case _ =>
}
chunkWatchers.removeBinding(p.getEntityId, c)
}
def getDescPacket(chunk: Chunk, it: Iterator[TileEntity]): PacketCustom = {
val s = new MCByteStream(new ByteArrayOutputStream)
var num = 0
while (it.hasNext) {
val tile = it.next
if (tile.isInstanceOf[TileMultipart]) {
s.writeShort(indexInChunk(new BlockCoord(tile)))
tile.asInstanceOf[TileMultipart].writeDesc(s)
num += 1
}
}
if (num != 0) {
return new PacketCustom(channel, 2)
.compress()
.writeInt(chunk.xPosition)
.writeInt(chunk.zPosition)
.writeShort(num)
.writeByteArray(s.getBytes)
}
return null
}
}
@@ -18,73 +18,110 @@ import cpw.mods.fml.common.registry.GameRegistry
import net.minecraft.block.Block
import org.apache.logging.log4j.Logger
class MultipartProxy_serverImpl
{
var block:BlockMultipart = _
var config:ConfigFile = _
var logger:Logger = _
class MultipartProxy_serverImpl {
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")
def preInit(cfgdir: File, logger: Logger) {
this.logger = logger
config = new ConfigFile(new File(cfgdir, "multipart.cfg"))
.setComment("Multipart API config file")
GameRegistry.registerBlock(new BlockMultipart().setBlockName("multipart"), null, "block")
block = Block.blockRegistry.getObject("ForgeMultipart:block").asInstanceOf[BlockMultipart]
GameRegistry.registerBlock(
new BlockMultipart().setBlockName("multipart"),
null,
"block"
)
block = Block.blockRegistry
.getObject("ForgeMultipart:block")
.asInstanceOf[BlockMultipart]
MultipartGenerator.registerTrait("codechicken.multipart.TSlottedPart", "codechicken.multipart.scalatraits.TSlottedTile")
MultipartGenerator.registerTrait("net.minecraftforge.fluids.IFluidHandler", "codechicken.multipart.scalatraits.TFluidHandlerTile")
MultipartGenerator.registerTrait("net.minecraft.inventory.IInventory", "codechicken.multipart.scalatraits.JInventoryTile")
MultipartGenerator.registerTrait("net.minecraft.inventory.ISidedInventory", "codechicken.multipart.scalatraits.JInventoryTile")
MultipartGenerator.registerTrait("codechicken.multipart.JPartialOcclusion", "codechicken.multipart.scalatraits.TPartialOcclusionTile")
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")
MultipartGenerator.registerTrait(
"codechicken.multipart.TSlottedPart",
"codechicken.multipart.scalatraits.TSlottedTile"
)
MultipartGenerator.registerTrait(
"net.minecraftforge.fluids.IFluidHandler",
"codechicken.multipart.scalatraits.TFluidHandlerTile"
)
MultipartGenerator.registerTrait(
"net.minecraft.inventory.IInventory",
"codechicken.multipart.scalatraits.JInventoryTile"
)
MultipartGenerator.registerTrait(
"net.minecraft.inventory.ISidedInventory",
"codechicken.multipart.scalatraits.JInventoryTile"
)
MultipartGenerator.registerTrait(
"codechicken.multipart.JPartialOcclusion",
"codechicken.multipart.scalatraits.TPartialOcclusionTile"
)
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()
}
MultipartSaveLoad.hookLoader()
}
def init() {}
def init() {}
def postInit() {
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
PacketCustom.assignHandshakeHandler(MultipartSPH.registryChannel, MultipartSPH)
def postInit() {
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
PacketCustom.assignHandshakeHandler(
MultipartSPH.registryChannel,
MultipartSPH
)
WorldExtensionManager.registerWorldExtension(TickScheduler)
TileChunkLoadHook.init()
WorldExtensionManager.registerWorldExtension(TickScheduler)
TileChunkLoadHook.init()
MultipartCompatiblity.load()
}
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() {
super.postInit()
RenderingRegistry.registerBlockHandler(MultipartRenderer)
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
PacketCustom.assignHandler(MultipartCPH.registryChannel, MultipartCPH)
class MultipartProxy_clientImpl extends MultipartProxy_serverImpl {
@SideOnly(Side.CLIENT)
override def postInit() {
super.postInit()
RenderingRegistry.registerBlockHandler(MultipartRenderer)
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
PacketCustom.assignHandler(MultipartCPH.registryChannel, MultipartCPH)
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
ClientRegistry.registerKeyBinding(ControlKeyHandler)
}
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
ClientRegistry.registerKeyBinding(ControlKeyHandler)
}
@SideOnly(Side.CLIENT)
override def onTileClassBuilt(t: Class[_ <: TileEntity]) {
super.onTileClassBuilt(t)
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
}
@SideOnly(Side.CLIENT)
override def onTileClassBuilt(t: Class[_ <: TileEntity]) {
super.onTileClassBuilt(t)
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
}
}
object MultipartProxy extends MultipartProxy_clientImpl
{
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
object MultipartProxy extends MultipartProxy_clientImpl {
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
}
@@ -1,9 +1,5 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.IFaceRedstonePart;
import net.minecraft.block.Block;
import net.minecraft.block.BlockButton;
import net.minecraft.entity.Entity;
@@ -15,11 +11,17 @@ import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.IFaceRedstonePart;
public class ButtonPart extends McSidedMetaPart implements IFaceRedstonePart {
public static BlockButton stoneButton = (BlockButton) Blocks.stone_button;
public static BlockButton woodenButton = (BlockButton) Blocks.wooden_button;
public static int[] metaSideMap = new int[] {-1, 4, 5, 2, 3};
public static int[] sideMetaMap = new int[] {-1, -1, 3, 4, 1, 2};
public static int[] metaSideMap = new int[] { -1, 4, 5, 2, 3 };
public static int[] sideMetaMap = new int[] { -1, -1, 3, 4, 1, 2 };
public static BlockButton getButton(int meta) {
return (meta & 0x10) > 0 ? woodenButton : stoneButton;
@@ -112,11 +114,9 @@ public class ButtonPart extends McSidedMetaPart implements IFaceRedstonePart {
}
private void updateState() {
boolean arrows = sensitive()
&& !world().getEntitiesWithinAABB(
EntityArrow.class,
getBounds().add(Vector3.fromTileEntity(tile())).toAABB())
.isEmpty();
boolean arrows = sensitive() && !world()
.getEntitiesWithinAABB(EntityArrow.class, getBounds().add(Vector3.fromTileEntity(tile())).toAABB())
.isEmpty();
boolean pressed = pressed();
if (arrows != pressed) toggle();
@@ -1,16 +1,19 @@
package codechicken.multipart.minecraft;
import java.util.Arrays;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import codechicken.lib.vec.BlockCoord;
import codechicken.multipart.MultiPartRegistry;
import codechicken.multipart.MultiPartRegistry.IPartConverter;
import codechicken.multipart.MultiPartRegistry.IPartFactory;
import codechicken.multipart.TMultiPart;
import java.util.Arrays;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
public class Content implements IPartFactory, IPartConverter {
@Override
public TMultiPart createPart(String name, boolean client) {
if (name.equals("mc_torch")) return new TorchPart();
@@ -23,7 +26,7 @@ public class Content implements IPartFactory, IPartConverter {
public void init() {
MultiPartRegistry.registerConverter(this);
MultiPartRegistry.registerParts(this, new String[] {"mc_torch", "mc_lever", "mc_button", "mc_redtorch"});
MultiPartRegistry.registerParts(this, new String[] { "mc_torch", "mc_lever", "mc_button", "mc_redtorch" });
}
@Override
@@ -1,12 +1,5 @@
package codechicken.multipart.minecraft;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.raytracer.RayTracer;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.TileMultipart;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.block.Block;
import net.minecraft.block.BlockFence;
import net.minecraft.entity.player.EntityPlayer;
@@ -20,7 +13,16 @@ import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.raytracer.RayTracer;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.TileMultipart;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class EventHandler {
private ThreadLocal<Object> placing = new ThreadLocal<Object>();
@SubscribeEvent(priority = EventPriority.LOW)
@@ -53,33 +55,32 @@ public class EventHandler {
if (part == null) return false;
if (world.isRemote
&& !player
.isSneaking()) // attempt to use block activated like normal and tell the server the right stuff
if (world.isRemote && !player.isSneaking()) // attempt to use block activated like normal and tell the server
// the right stuff
{
Vector3 f = new Vector3(hit.hitVec).add(-hit.blockX, -hit.blockY, -hit.blockZ);
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
if (!ignoreActivate(block)
&& block.onBlockActivated(
world,
hit.blockX,
hit.blockY,
hit.blockZ,
player,
hit.sideHit,
(float) f.x,
(float) f.y,
(float) f.z)) {
if (!ignoreActivate(block) && block.onBlockActivated(
world,
hit.blockX,
hit.blockY,
hit.blockZ,
player,
hit.sideHit,
(float) f.x,
(float) f.y,
(float) f.z)) {
player.swingItem();
PacketCustom.sendToServer(new C08PacketPlayerBlockPlacement(
hit.blockX,
hit.blockY,
hit.blockZ,
hit.sideHit,
player.inventory.getCurrentItem(),
(float) f.x,
(float) f.y,
(float) f.z));
PacketCustom.sendToServer(
new C08PacketPlayerBlockPlacement(
hit.blockX,
hit.blockY,
hit.blockZ,
hit.sideHit,
player.inventory.getCurrentItem(),
(float) f.x,
(float) f.y,
(float) f.z));
return true;
}
}
@@ -1,10 +1,12 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import codechicken.lib.vec.BlockCoord;
public interface IPartMeta {
public int getMetadata();
public World getWorld();
@@ -1,9 +1,5 @@
package codechicken.multipart.minecraft;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLever;
import net.minecraft.client.renderer.RenderBlocks;
@@ -15,11 +11,17 @@ import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.math.MathHelper;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
public class LeverPart extends McSidedMetaPart implements IFaceRedstonePart {
public static BlockLever lever = (BlockLever) Blocks.lever;
public static int[] metaSideMap = new int[] {1, 4, 5, 2, 3, 0, 0, 1};
public static int[] sideMetaMap = new int[] {6, 0, 3, 4, 1, 2};
public static int[] metaSwapMap = new int[] {5, 7};
public static int[] metaSideMap = new int[] { 1, 4, 5, 2, 3, 0, 0, 1 };
public static int[] sideMetaMap = new int[] { 6, 0, 3, 4, 1, 2 };
public static int[] metaSwapMap = new int[] { 5, 7 };
public LeverPart() {}
@@ -1,5 +1,15 @@
package codechicken.multipart.minecraft;
import java.util.Arrays;
import java.util.Collections;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.MovingObjectPosition;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IconHitEffects;
import codechicken.multipart.JCuboidPart;
@@ -9,16 +19,9 @@ import codechicken.multipart.NormalOcclusionTest;
import codechicken.multipart.TMultiPart;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.Arrays;
import java.util.Collections;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.MovingObjectPosition;
public abstract class McBlockPart extends JCuboidPart implements JNormalOcclusion, JIconHitEffects {
public abstract Block getBlock();
@Override
@@ -1,14 +1,16 @@
package codechicken.multipart.minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import codechicken.lib.data.MCDataInput;
import codechicken.lib.data.MCDataOutput;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
public abstract class McMetaPart extends McBlockPart implements IPartMeta {
public byte meta;
public McMetaPart() {}
@@ -1,13 +1,15 @@
package codechicken.multipart.minecraft;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.lib.vec.BlockCoord;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.network.play.INetHandlerPlayClient;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.lib.vec.BlockCoord;
public class McMultipartCPH implements IClientPacketHandler {
public static Object channel = MinecraftMultipartMod.instance;
@Override
@@ -20,14 +22,13 @@ public class McMultipartCPH implements IClientPacketHandler {
}
private void spawnBurnoutSmoke(WorldClient world, BlockCoord pos) {
for (int l = 0; l < 5; l++)
world.spawnParticle(
"smoke",
pos.x + world.rand.nextDouble() * 0.6 + 0.2,
pos.y + world.rand.nextDouble() * 0.6 + 0.2,
pos.z + world.rand.nextDouble() * 0.6 + 0.2,
0,
0,
0);
for (int l = 0; l < 5; l++) world.spawnParticle(
"smoke",
pos.x + world.rand.nextDouble() * 0.6 + 0.2,
pos.y + world.rand.nextDouble() * 0.6 + 0.2,
pos.z + world.rand.nextDouble() * 0.6 + 0.2,
0,
0,
0);
}
}
@@ -1,12 +1,14 @@
package codechicken.multipart.minecraft;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.play.INetHandlerPlayServer;
import net.minecraft.world.World;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
public class McMultipartSPH implements IServerPacketHandler {
public static Object channel = MinecraftMultipartMod.instance;
@Override
@@ -1,13 +1,15 @@
package codechicken.multipart.minecraft;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.TFacePart;
import codechicken.multipart.TileMultipart;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.ForgeDirection;
public abstract class McSidedMetaPart extends McMetaPart implements TFacePart {
public McSidedMetaPart() {}
public McSidedMetaPart(int meta) {
@@ -1,14 +1,16 @@
package codechicken.multipart.minecraft;
import net.minecraftforge.common.MinecraftForge;
import codechicken.lib.packet.PacketCustom;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.common.MinecraftForge;
@Mod(modid = "McMultipart", acceptedMinecraftVersions = "[1.7.10]")
public class MinecraftMultipartMod {
@Instance("McMultipart")
public static MinecraftMultipartMod instance;
@@ -1,15 +1,17 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PartMetaAccess implements IBlockAccess {
public IPartMeta part;
private BlockCoord pos;
@@ -1,12 +1,7 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
import codechicken.multipart.RedstoneInteractions;
import codechicken.multipart.TRandomUpdateTick;
import codechicken.multipart.TickScheduler;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRedstoneTorch;
import net.minecraft.init.Blocks;
@@ -15,11 +10,20 @@ import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
import codechicken.multipart.RedstoneInteractions;
import codechicken.multipart.TRandomUpdateTick;
import codechicken.multipart.TickScheduler;
public class RedstoneTorchPart extends TorchPart implements IFaceRedstonePart, TRandomUpdateTick {
public static BlockRedstoneTorch torchActive = (BlockRedstoneTorch) Blocks.redstone_torch;
public static BlockRedstoneTorch torchIdle = (BlockRedstoneTorch) Blocks.unlit_redstone_torch;
public class BurnoutEntry {
public BurnoutEntry(long l) {
timeout = l;
}
@@ -1,19 +1,22 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IRandomDisplayTick;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockTorch;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IRandomDisplayTick;
public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
public static BlockTorch torch = (BlockTorch) Blocks.torch;
public static int[] metaSideMap = new int[] {-1, 4, 5, 2, 3, 0};
public static int[] sideMetaMap = new int[] {5, 0, 3, 4, 1, 2};
public static int[] metaSideMap = new int[] { -1, 4, 5, 2, 3, 0 };
public static int[] sideMetaMap = new int[] { 5, 0, 3, 4, 1, 2 };
public TorchPart() {}
@@ -55,7 +58,7 @@ public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
@Override
public boolean canStay() {
return sideForMeta(meta) == 0
&& world().getBlock(x(), y() - 1, z()).canPlaceTorchOnTop(world(), x(), y() - 1, z())
&& world().getBlock(x(), y() - 1, z()).canPlaceTorchOnTop(world(), x(), y() - 1, z())
|| super.canStay();
}
@@ -64,7 +67,8 @@ public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
pos = pos.copy().offset(side ^ 1);
Block block = world.getBlock(pos.x, pos.y, pos.z);
if (!block.isSideSolid(world, pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side))
&& (side != 1 || block.canPlaceTorchOnTop(world, pos.x, pos.y, pos.z))) return null;
&& (side != 1 || block.canPlaceTorchOnTop(world, pos.x, pos.y, pos.z)))
return null;
return new TorchPart(sideMetaMap[side ^ 1]);
}
@@ -1,5 +1,9 @@
package codechicken.multipart.nei;
import java.util.Arrays;
import net.minecraft.util.StatCollector;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.microblock.CommonMicroClass;
import codechicken.microblock.MicroblockClass;
@@ -7,10 +11,9 @@ import codechicken.microblock.handler.MicroblockProxy;
import codechicken.nei.ItemStackMap;
import codechicken.nei.api.API;
import codechicken.nei.api.IConfigureNEI;
import java.util.Arrays;
import net.minecraft.util.StatCollector;
public class NEI_MicroblockConfig implements IConfigureNEI {
@Override
public void loadConfig() {
MicroblockClass[] microClasses = CommonMicroClass.classes();
@@ -2,7 +2,6 @@ package codechicken
import codechicken.multipart.handler.MultipartProxy
package object multipart
{
def logger = MultipartProxy.logger
package object multipart {
def logger = MultipartProxy.logger
}
@@ -10,121 +10,130 @@ import net.minecraftforge.fluids.Fluid
import java.util.LinkedList
import scala.collection.JavaConversions._
/**
* Mixin trait implementation for parts implementing IFluidHandler.
* Distributes fluid manipulation among fluid handling parts.
*/
trait TFluidHandlerTile extends TileMultipart with IFluidHandler
{
var tankList = new LinkedList[IFluidHandler]()
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TFluidHandlerTile])
tankList = that.asInstanceOf[TFluidHandlerTile].tankList
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[IFluidHandler])
tankList+=part.asInstanceOf[IFluidHandler]
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[IFluidHandler])
tankList-=part.asInstanceOf[IFluidHandler]
}
override def clearParts()
{
super.clearParts()
tankList.clear()
}
override def getTankInfo(dir:ForgeDirection):Array[FluidTankInfo] =
{
var tankCount:Int = 0
tankList.foreach(t => tankCount += t.getTankInfo(dir).length)
val tanks = new Array[FluidTankInfo](tankCount)
var i = 0
tankList.foreach(p => p.getTankInfo(dir).foreach{t =>
tanks(i) = t
i+=1
})
return tanks
}
override def fill(dir:ForgeDirection, liquid:FluidStack, doFill:Boolean):Int =
{
var filled = 0
val initial = liquid.amount
tankList.foreach(p =>
filled+=p.fill(dir, copy(liquid, initial-filled), doFill)
)
return filled
}
override def canFill(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canFill(dir, liquid)).isDefined
override def canDrain(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canDrain(dir, liquid)).isDefined
private def copy(liquid:FluidStack, quantity:Int):FluidStack =
{
val copy = liquid.copy
copy.amount = quantity
return copy
}
/** Mixin trait implementation for parts implementing IFluidHandler. Distributes
* fluid manipulation among fluid handling parts.
*/
trait TFluidHandlerTile extends TileMultipart with IFluidHandler {
var tankList = new LinkedList[IFluidHandler]()
override def drain(dir:ForgeDirection, amount:Int, doDrain:Boolean):FluidStack =
{
var drained:FluidStack = null
var d_amount = 0
tankList.foreach{p =>
val drain = amount-d_amount
val ret = p.drain(dir, drain, false)
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
{
if(doDrain)
p.drain(dir, drain, true)
override def copyFrom(that: TileMultipart) {
super.copyFrom(that)
if (that.isInstanceOf[TFluidHandlerTile])
tankList = that.asInstanceOf[TFluidHandlerTile].tankList
}
if(drained == null)
drained = ret
override def bindPart(part: TMultiPart) {
super.bindPart(part)
if (part.isInstanceOf[IFluidHandler])
tankList += part.asInstanceOf[IFluidHandler]
}
d_amount+=ret.amount
}
}
if(drained != null)
drained.amount = d_amount
override def partRemoved(part: TMultiPart, p: Int) {
super.partRemoved(part, p)
if (part.isInstanceOf[IFluidHandler])
tankList -= part.asInstanceOf[IFluidHandler]
}
return drained
override def clearParts() {
super.clearParts()
tankList.clear()
}
override def getTankInfo(dir: ForgeDirection): Array[FluidTankInfo] = {
var tankCount: Int = 0
tankList.foreach(t => tankCount += t.getTankInfo(dir).length)
val tanks = new Array[FluidTankInfo](tankCount)
var i = 0
tankList.foreach(p =>
p.getTankInfo(dir).foreach { t =>
tanks(i) = t
i += 1
}
)
return tanks
}
override def fill(
dir: ForgeDirection,
liquid: FluidStack,
doFill: Boolean
): Int = {
var filled = 0
val initial = liquid.amount
tankList.foreach(p =>
filled += p.fill(dir, copy(liquid, initial - filled), doFill)
)
return filled
}
override def canFill(dir: ForgeDirection, liquid: Fluid) =
tankList.find(_.canFill(dir, liquid)).isDefined
override def canDrain(dir: ForgeDirection, liquid: Fluid) =
tankList.find(_.canDrain(dir, liquid)).isDefined
private def copy(liquid: FluidStack, quantity: Int): FluidStack = {
val copy = liquid.copy
copy.amount = quantity
return copy
}
override def drain(
dir: ForgeDirection,
amount: Int,
doDrain: Boolean
): FluidStack = {
var drained: FluidStack = null
var d_amount = 0
tankList.foreach { p =>
val drain = amount - d_amount
val ret = p.drain(dir, drain, false)
if (
ret != null && ret.amount > 0 && (drained == null || drained
.isFluidEqual(ret))
) {
if (doDrain)
p.drain(dir, drain, true)
if (drained == null)
drained = ret
d_amount += ret.amount
}
}
if (drained != null)
drained.amount = d_amount
override def drain(dir:ForgeDirection, fluid:FluidStack, doDrain:Boolean):FluidStack =
{
val amount = fluid.amount
var drained:FluidStack = null
var d_amount = 0
tankList.foreach{p =>
val drain = copy(fluid, amount-d_amount)
val ret = p.drain(dir, drain, false)
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
{
if(doDrain)
p.drain(dir, drain, true)
return drained
}
if(drained == null)
drained = ret
override def drain(
dir: ForgeDirection,
fluid: FluidStack,
doDrain: Boolean
): FluidStack = {
val amount = fluid.amount
var drained: FluidStack = null
var d_amount = 0
tankList.foreach { p =>
val drain = copy(fluid, amount - d_amount)
val ret = p.drain(dir, drain, false)
if (
ret != null && ret.amount > 0 && (drained == null || drained
.isFluidEqual(ret))
) {
if (doDrain)
p.drain(dir, drain, true)
d_amount+=ret.amount
}
}
if(drained != null)
drained.amount = d_amount
if (drained == null)
drained = ret
return drained
d_amount += ret.amount
}
}
}
if (drained != null)
drained.amount = d_amount
return drained
}
}
@@ -8,126 +8,119 @@ import net.minecraft.entity.player.EntityPlayer
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
trait TIInventoryTile extends TileMultipart with ISidedInventory
{
var invList = new LinkedList[IInventory]()
var slotMap = Array[(IInventory, Int)]()
trait TIInventoryTile extends TileMultipart with ISidedInventory {
var invList = new LinkedList[IInventory]()
var slotMap = Array[(IInventory, Int)]()
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TIInventoryTile]) {
invList = that.asInstanceOf[TIInventoryTile].invList
rebuildSlotMap()
}
override def copyFrom(that: TileMultipart) {
super.copyFrom(that)
if (that.isInstanceOf[TIInventoryTile]) {
invList = that.asInstanceOf[TIInventoryTile].invList
rebuildSlotMap()
}
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[IInventory]) {
invList+=part.asInstanceOf[IInventory]
rebuildSlotMap()
}
override def bindPart(part: TMultiPart) {
super.bindPart(part)
if (part.isInstanceOf[IInventory]) {
invList += part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[IInventory]) {
invList-=part.asInstanceOf[IInventory]
rebuildSlotMap()
}
override def partRemoved(part: TMultiPart, p: Int) {
super.partRemoved(part, p)
if (part.isInstanceOf[IInventory]) {
invList -= part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def clearParts()
{
super.clearParts()
invList.clear()
slotMap = Array()
override def clearParts() {
super.clearParts()
invList.clear()
slotMap = Array()
}
def rebuildSlotMap() {
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
var i = 0
for (inv <- invList; s <- 0 until inv.getSizeInventory) {
slotMap(i) = (inv, s)
i += 1
}
}
def rebuildSlotMap() {
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
var i = 0
for(inv <- invList; s <- 0 until inv.getSizeInventory) {
slotMap(i) = (inv, s)
i+=1
}
def getSizeInventory: Int = slotMap.length
def getStackInSlot(i: Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlot(slot)
}
def decrStackSize(i: Int, j: Int) = {
val (inv, slot) = slotMap(i)
inv.decrStackSize(slot, j)
}
def getStackInSlotOnClosing(i: Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlotOnClosing(slot)
}
def setInventorySlotContents(i: Int, itemstack: ItemStack) = {
val (inv, slot) = slotMap(i)
inv.setInventorySlotContents(slot, itemstack)
}
def getInventoryName = "Multipart Inventory"
def hasCustomInventoryName = false
def getInventoryStackLimit = 64
def isUseableByPlayer(entityplayer: EntityPlayer) = true
def openInventory() {}
def closeInventory() {}
def isItemValidForSlot(i: Int, itemstack: ItemStack) = {
val (inv, slot) = slotMap(i)
inv.isItemValidForSlot(slot, itemstack)
}
def getAccessibleSlotsFromSide(side: Int) = {
val buf = new ArrayBuffer[Int]()
var base = 0
for (inv <- invList) {
inv match {
case isided: ISidedInventory =>
buf ++= isided.getAccessibleSlotsFromSide(side).map(_ + base)
case _ =>
}
base += inv.getSizeInventory
}
buf.toArray
}
def getSizeInventory:Int = slotMap.length
def getStackInSlot(i:Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlot(slot)
def canInsertItem(i: Int, itemstack: ItemStack, j: Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided: ISidedInventory => isided.canInsertItem(slot, itemstack, j)
case _ => true
}
}
def decrStackSize(i:Int, j:Int) = {
val (inv, slot) = slotMap(i)
inv.decrStackSize(slot, j)
}
def getStackInSlotOnClosing(i:Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlotOnClosing(slot)
}
def setInventorySlotContents(i:Int, itemstack:ItemStack) = {
val (inv, slot) = slotMap(i)
inv.setInventorySlotContents(slot, itemstack)
}
def getInventoryName = "Multipart Inventory"
def hasCustomInventoryName = false
def getInventoryStackLimit = 64
def isUseableByPlayer(entityplayer:EntityPlayer) = true
def openInventory(){}
def closeInventory(){}
def isItemValidForSlot(i:Int, itemstack:ItemStack) = {
val (inv, slot) = slotMap(i)
inv.isItemValidForSlot(slot, itemstack)
}
def getAccessibleSlotsFromSide(side:Int) = {
val buf = new ArrayBuffer[Int]()
var base = 0
for(inv <- invList) {
inv match {
case isided:ISidedInventory => buf++=isided.getAccessibleSlotsFromSide(side).map(_ + base)
case _ =>
}
base+=inv.getSizeInventory
}
buf.toArray
}
def canInsertItem(i:Int, itemstack:ItemStack, j:Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided:ISidedInventory => isided.canInsertItem(slot, itemstack, j)
case _ => true
}
}
def canExtractItem(i:Int, itemstack:ItemStack, j:Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided:ISidedInventory => isided.canExtractItem(slot, itemstack, j)
case _ => true
}
def canExtractItem(i: Int, itemstack: ItemStack, j: Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided: ISidedInventory => isided.canExtractItem(slot, itemstack, j)
case _ => true
}
}
}
/**
* To handle obfuscation issues, this is registered as a java trait.
*/
class JInventoryTile extends TileMultipart with TIInventoryTile
{
}
/** To handle obfuscation issues, this is registered as a java trait.
*/
class JInventoryTile extends TileMultipart with TIInventoryTile {}
@@ -5,30 +5,32 @@ import codechicken.multipart.TMultiPart
import codechicken.multipart.JPartialOcclusion
import codechicken.multipart.PartialOcclusionTest
/**
* Implementation for the partial occlusion test.
*/
class TPartialOcclusionTile extends TileMultipart
{
override def occlusionTest(parts:Seq[TMultiPart], npart:TMultiPart):Boolean =
{
if(npart.isInstanceOf[JPartialOcclusion] && !partialOcclusionTest(parts:+npart))
return false
return super.occlusionTest(parts, npart)
/** Implementation for the partial occlusion test.
*/
class TPartialOcclusionTile extends TileMultipart {
override def occlusionTest(
parts: Seq[TMultiPart],
npart: TMultiPart
): Boolean = {
if (
npart.isInstanceOf[JPartialOcclusion] && !partialOcclusionTest(
parts :+ npart
)
)
return false
return super.occlusionTest(parts, npart)
}
def partialOcclusionTest(parts: Seq[TMultiPart]): Boolean = {
val test = new PartialOcclusionTest(parts.length)
var i = 0
while (i < parts.length) {
val part = parts(i)
if (part.isInstanceOf[JPartialOcclusion])
test.fill(i, part.asInstanceOf[JPartialOcclusion])
i += 1
}
def partialOcclusionTest(parts:Seq[TMultiPart]):Boolean =
{
val test = new PartialOcclusionTest(parts.length)
var i = 0
while(i < parts.length)
{
val part = parts(i)
if(part.isInstanceOf[JPartialOcclusion])
test.fill(i, part.asInstanceOf[JPartialOcclusion])
i+=1
}
return test()
}
}
return test()
}
}
@@ -4,14 +4,11 @@ import codechicken.multipart.TileMultipartClient
import codechicken.multipart.IRandomDisplayTick
import java.util.Random
/**
* Saves processor time looping on tiles that don't need it
*/
trait TRandomDisplayTickTile extends TileMultipartClient
{
override def randomDisplayTick(random:Random)
{
for(p@(_p: IRandomDisplayTick) <- partList.iterator)
p.randomDisplayTick(random)
}
}
/** Saves processor time looping on tiles that don't need it
*/
trait TRandomDisplayTickTile extends TileMultipartClient {
override def randomDisplayTick(random: Random) {
for (p @ (_p: IRandomDisplayTick) <- partList.iterator)
p.randomDisplayTick(random)
}
}
@@ -9,79 +9,78 @@ import codechicken.multipart.IRedstoneTile
import codechicken.multipart.TFacePart
import codechicken.multipart.TEdgePart
/**
* Mixin trait implementation for IRedstonePart
* Provides and overrides various redstone functions.
*/
trait TRedstoneTile extends TileMultipart with IRedstoneTile
{
/**
* Returns the strong (indirect) power being emitted fr
*/
override def strongPowerLevel(side:Int):Int =
{
var max = 0
for(p@(_p: IRedstonePart) <- partList.iterator)
{
val l = p.strongPowerLevel(side)
if(l > max) max = l
}
return max
}
def openConnections(side:Int):Int =
{
var m = 0x10
var i = 0
while(i < 4)
{
if(redstoneConductionE(edgeBetween(side, rotateSide(side&6, i))))
m|=1<<i
i+=1
}
m&=redstoneConductionF(side)
return m
}
def redstoneConductionF(i:Int) = partMap(i) match {
case null => 0x1F
case p => p.asInstanceOf[TFacePart].redstoneConductionMap
}
def redstoneConductionE(i:Int) = partMap(i) match {
case null => true
case p => p.asInstanceOf[TEdgePart].conductsRedstone
}
override def weakPowerLevel(side:Int):Int =
weakPowerLevel(side, otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, side, true))
override def canConnectRedstone(side:Int):Boolean =
{
val vside = vanillaToSide(side)
return (getConnectionMask(vside) & otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, vside, false)) > 0
}
def getConnectionMask(side:Int):Int =
{
val mask = openConnections(side)
var res = 0
partList.foreach(p =>
res|=connectionMask(p, side)&mask)
return res
}
def weakPowerLevel(side:Int, mask:Int):Int =
{
val tmask = openConnections(side)&mask
var max = 0
partList.foreach(p =>
if((connectionMask(p, side)&tmask) > 0)
{
val l = p.asInstanceOf[IRedstonePart].weakPowerLevel(side)
if(l > max) max = l
})
return max
}
}
/** Mixin trait implementation for IRedstonePart Provides and overrides various
* redstone functions.
*/
trait TRedstoneTile extends TileMultipart with IRedstoneTile {
/** Returns the strong (indirect) power being emitted fr
*/
override def strongPowerLevel(side: Int): Int = {
var max = 0
for (p @ (_p: IRedstonePart) <- partList.iterator) {
val l = p.strongPowerLevel(side)
if (l > max) max = l
}
return max
}
def openConnections(side: Int): Int = {
var m = 0x10
var i = 0
while (i < 4) {
if (redstoneConductionE(edgeBetween(side, rotateSide(side & 6, i))))
m |= 1 << i
i += 1
}
m &= redstoneConductionF(side)
return m
}
def redstoneConductionF(i: Int) = partMap(i) match {
case null => 0x1f
case p => p.asInstanceOf[TFacePart].redstoneConductionMap
}
def redstoneConductionE(i: Int) = partMap(i) match {
case null => true
case p => p.asInstanceOf[TEdgePart].conductsRedstone
}
override def weakPowerLevel(side: Int): Int =
weakPowerLevel(
side,
otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, side, true)
)
override def canConnectRedstone(side: Int): Boolean = {
val vside = vanillaToSide(side)
return (getConnectionMask(vside) & otherConnectionMask(
getWorldObj,
xCoord,
yCoord,
zCoord,
vside,
false
)) > 0
}
def getConnectionMask(side: Int): Int = {
val mask = openConnections(side)
var res = 0
partList.foreach(p => res |= connectionMask(p, side) & mask)
return res
}
def weakPowerLevel(side: Int, mask: Int): Int = {
val tmask = openConnections(side) & mask
var max = 0
partList.foreach(p =>
if ((connectionMask(p, side) & tmask) > 0) {
val l = p.asInstanceOf[IRedstonePart].weakPowerLevel(side)
if (l > max) max = l
}
)
return max
}
}
@@ -4,61 +4,52 @@ import codechicken.multipart.TileMultipart
import codechicken.multipart.TMultiPart
import codechicken.multipart.TSlottedPart
/**
* Mixin implementation for TSlottedPart.
* Puts parts into a slot array for quick access at the cost of memory consumption
*/
trait TSlottedTile extends TileMultipart
{
var v_partMap = new Array[TMultiPart](27)
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TSlottedTile])
v_partMap = that.asInstanceOf[TSlottedTile].v_partMap
/** Mixin implementation for TSlottedPart. Puts parts into a slot array for
* quick access at the cost of memory consumption
*/
trait TSlottedTile extends TileMultipart {
var v_partMap = new Array[TMultiPart](27)
override def copyFrom(that: TileMultipart) {
super.copyFrom(that)
if (that.isInstanceOf[TSlottedTile])
v_partMap = that.asInstanceOf[TSlottedTile].v_partMap
}
override def partMap(slot: Int) = v_partMap(slot)
override def clearParts() {
super.clearParts()
for (i <- 0 until v_partMap.length)
v_partMap(i) = null
}
override def partRemoved(part: TMultiPart, p: Int) {
super.partRemoved(part, p)
if (part.isInstanceOf[TSlottedPart])
for (i <- 0 until 27)
if (partMap(i) == part)
v_partMap(i) = null
}
override def canAddPart(part: TMultiPart): Boolean = {
if (part.isInstanceOf[TSlottedPart]) {
val slotMask = part.asInstanceOf[TSlottedPart].getSlotMask
for (i <- 0 until v_partMap.length)
if ((slotMask & 1 << i) != 0 && partMap(i) != null)
return false
}
override def partMap(slot:Int) = v_partMap(slot)
override def clearParts()
{
super.clearParts()
for(i <- 0 until v_partMap.length)
v_partMap(i) = null
return super.canAddPart(part)
}
override def bindPart(part: TMultiPart) {
super.bindPart(part)
if (part.isInstanceOf[TSlottedPart]) {
val mask = part.asInstanceOf[TSlottedPart].getSlotMask
for (i <- 0 until 27)
if ((mask & 1 << i) > 0)
v_partMap(i) = part
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[TSlottedPart])
for(i <- 0 until 27)
if(partMap(i) == part)
v_partMap(i) = null
}
override def canAddPart(part:TMultiPart):Boolean =
{
if(part.isInstanceOf[TSlottedPart])
{
val slotMask = part.asInstanceOf[TSlottedPart].getSlotMask
for(i <- 0 until v_partMap.length)
if((slotMask&1<<i) != 0 && partMap(i) != null)
return false
}
return super.canAddPart(part)
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[TSlottedPart])
{
val mask = part.asInstanceOf[TSlottedPart].getSlotMask
for(i <- 0 until 27)
if ((mask&1<<i) > 0)
v_partMap(i) = part
}
}
}
}
}
@@ -5,55 +5,54 @@ import codechicken.multipart.INeighborTileChange
import codechicken.multipart.TileMultipart
import codechicken.lib.vec.BlockCoord
/**
* Mixin implementation for INeighborTileChange
*
* Reduces unnecessary computation
*/
/** Mixin implementation for INeighborTileChange
*
* Reduces unnecessary computation
*/
trait TTileChangeTile extends TileMultipart {
var weakTileChanges = false
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TTileChangeTile])
weakTileChanges = that.asInstanceOf[TTileChangeTile].weakTileChanges
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[INeighborTileChange])
weakTileChanges|=part.asInstanceOf[INeighborTileChange].weakTileChanges
}
override def clearParts()
{
super.clearParts()
weakTileChanges = false
}
override def partRemoved(part:TMultiPart, p:Int) {
super.partRemoved(part, p)
weakTileChanges = partList.exists(p => p.isInstanceOf[INeighborTileChange] && p.asInstanceOf[INeighborTileChange].weakTileChanges)
}
override def onNeighborTileChange(tileX:Int, tileY:Int, tileZ:Int)
{
super.onNeighborTileChange(tileX, tileY, tileZ)
val offset = new BlockCoord(tileX, tileY, tileZ).sub(xCoord, yCoord, zCoord)
val diff = offset.absSum
val side = offset.toSide
if(side < 0 || diff <= 0 || diff > 2)
return
val weak = diff == 2
operate{ p =>
if(p.isInstanceOf[INeighborTileChange])
p.asInstanceOf[INeighborTileChange].onNeighborTileChanged(side, weak)
}
}
var weakTileChanges = false
override def getWeakChanges() = weakTileChanges
}
override def copyFrom(that: TileMultipart) {
super.copyFrom(that)
if (that.isInstanceOf[TTileChangeTile])
weakTileChanges = that.asInstanceOf[TTileChangeTile].weakTileChanges
}
override def bindPart(part: TMultiPart) {
super.bindPart(part)
if (part.isInstanceOf[INeighborTileChange])
weakTileChanges |= part.asInstanceOf[INeighborTileChange].weakTileChanges
}
override def clearParts() {
super.clearParts()
weakTileChanges = false
}
override def partRemoved(part: TMultiPart, p: Int) {
super.partRemoved(part, p)
weakTileChanges = partList.exists(p =>
p.isInstanceOf[INeighborTileChange] && p
.asInstanceOf[INeighborTileChange]
.weakTileChanges
)
}
override def onNeighborTileChange(tileX: Int, tileY: Int, tileZ: Int) {
super.onNeighborTileChange(tileX, tileY, tileZ)
val offset = new BlockCoord(tileX, tileY, tileZ).sub(xCoord, yCoord, zCoord)
val diff = offset.absSum
val side = offset.toSide
if (side < 0 || diff <= 0 || diff > 2)
return
val weak = diff == 2
operate { p =>
if (p.isInstanceOf[INeighborTileChange])
p.asInstanceOf[INeighborTileChange].onNeighborTileChanged(side, weak)
}
}
override def getWeakChanges() = weakTileChanges
}