blob: 0f49201aa33a74250044bd5604c31399d10f33dd [file] [log] [blame] [raw]
package li.cil.oc.common.tileentity
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.Driver
import li.cil.oc.api.internal
import li.cil.oc.api.network.Analyzable
import li.cil.oc.api.network._
import li.cil.oc.common.Slot
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.nbt.NBTTagList
import net.minecraftforge.common.util.Constants.NBT
import net.minecraftforge.common.util.ForgeDirection
import scala.collection.mutable
class Adapter extends traits.Environment with traits.ComponentInventory with Analyzable with internal.Adapter {
val node = api.Network.newNode(this, Visibility.Network).create()
private val blocks = Array.fill[Option[(ManagedEnvironment, api.driver.Block)]](6)(None)
private val updatingBlocks = mutable.ArrayBuffer.empty[ManagedEnvironment]
private val blocksData = Array.fill[Option[BlockData]](6)(None)
// ----------------------------------------------------------------------- //
override def onAnalyze(player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float) = blocks collect {
case Some(((environment, _))) => environment.node
}
// ----------------------------------------------------------------------- //
override def canUpdate = isServer
override def updateEntity() {
super.updateEntity()
if (updatingBlocks.nonEmpty) {
for (block <- updatingBlocks) {
block.update()
}
}
}
def neighborChanged(d: ForgeDirection) {
if (node != null && node.network != null) {
val (x, y, z) = (this.x + d.offsetX, this.y + d.offsetY, this.z + d.offsetZ)
world.getTileEntity(x, y, z) match {
case env: traits.Environment =>
// Don't provide adaption for our stuffs. This is mostly to avoid
// cables and other non-functional stuff popping up in the adapter
// due to having a power interface. Might revisit this at some point,
// but the only 'downside' is that it can't be used to manipulate
// inventories, which I actually consider a plus :P
case _ =>
Option(api.Driver.driverFor(world, x, y, z)) match {
case Some(newDriver) => blocks(d.ordinal()) match {
case Some((oldEnvironment, driver)) =>
if (newDriver != driver) {
// This is... odd. Maybe moved by some other mod? First, clean up.
blocks(d.ordinal()) = None
updatingBlocks -= oldEnvironment
blocksData(d.ordinal()) = None
node.disconnect(oldEnvironment.node)
// Then rebuild - if we have something.
val environment = newDriver.createEnvironment(world, x, y, z)
if (environment != null) {
blocks(d.ordinal()) = Some((environment, newDriver))
if (environment.canUpdate) {
updatingBlocks += environment
}
blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
node.connect(environment.node)
}
} // else: the more things change, the more they stay the same.
case _ =>
// A challenger appears. Maybe.
val environment = newDriver.createEnvironment(world, x, y, z)
if (environment != null) {
blocks(d.ordinal()) = Some((environment, newDriver))
if (environment.canUpdate) {
updatingBlocks += environment
}
blocksData(d.ordinal()) match {
case Some(data) if data.name == environment.getClass.getName =>
environment.load(data.data)
case _ =>
}
blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
node.connect(environment.node)
}
}
case _ => blocks(d.ordinal()) match {
case Some((environment, driver)) =>
// We had something there, but it's gone now...
node.disconnect(environment.node)
environment.save(blocksData(d.ordinal()).get.data)
Option(environment.node).foreach(_.remove())
blocks(d.ordinal()) = None
updatingBlocks -= environment
case _ => // Nothing before, nothing now.
}
}
}
}
}
def neighborChanged() {
if (node != null && node.network != null) {
for (d <- ForgeDirection.VALID_DIRECTIONS) {
neighborChanged(d)
}
}
}
// ----------------------------------------------------------------------- //
override def onConnect(node: Node) {
super.onConnect(node)
if (node == this.node) {
neighborChanged()
}
}
override def onDisconnect(node: Node) {
super.onDisconnect(node)
if (node == this.node) {
updatingBlocks.clear()
}
}
// ----------------------------------------------------------------------- //
override def getSizeInventory = 1
override def isItemValidForSlot(slot: Int, stack: ItemStack) = (slot, Option(Driver.driverFor(stack, getClass))) match {
case (0, Some(driver)) => driver.slot(stack) == Slot.Upgrade
case _ => false
}
// ----------------------------------------------------------------------- //
override def readFromNBTForServer(nbt: NBTTagCompound) {
super.readFromNBTForServer(nbt)
val blocksNbt = nbt.getTagList(Settings.namespace + "adapter.blocks", NBT.TAG_COMPOUND)
(0 until (blocksNbt.tagCount min blocksData.length)).
map(blocksNbt.getCompoundTagAt).
zipWithIndex.
foreach {
case (blockNbt, i) =>
if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) {
blocksData(i) = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data")))
}
}
}
override def writeToNBTForServer(nbt: NBTTagCompound) {
super.writeToNBTForServer(nbt)
val blocksNbt = new NBTTagList()
for (i <- 0 until blocks.length) {
val blockNbt = new NBTTagCompound()
blocksData(i) match {
case Some(data) =>
blocks(i) match {
case Some((environment, _)) => environment.save(data.data)
case _ =>
}
blockNbt.setString("name", data.name)
blockNbt.setTag("data", data.data)
case _ =>
}
blocksNbt.appendTag(blockNbt)
}
nbt.setTag(Settings.namespace + "adapter.blocks", blocksNbt)
}
// ----------------------------------------------------------------------- //
private class BlockData(val name: String, val data: NBTTagCompound)
}