blob: 36713d89d12ad8a5899fa4caedb413f42f2bf3a3 [file] [log] [blame] [raw]
package li.cil.oc.server.component
import java.util
import li.cil.oc.Constants
import li.cil.oc.api.driver.DeviceInfo
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.common.event.BlockChangeHandler
import li.cil.oc.common.event.BlockChangeHandler.ChangeListener
import li.cil.oc.server.network
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.Settings
import li.cil.oc.api
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.Vec3
import net.minecraftforge.common.util.ForgeDirection
import scala.collection.convert.WrapAsJava._
/**
* Mostly stolen from {@link li.cil.oc.common.tileentity.Adapter}
*
* @author Sangar, Vexatos
*/
class UpgradeMF(val host: EnvironmentHost, val coord: BlockPosition, val dir: ForgeDirection) extends prefab.ManagedEnvironment with ChangeListener with DeviceInfo {
override val node = api.Network.newNode(this, Visibility.None).
withConnector().
create()
private var otherEnv: Option[api.network.Environment] = None
private var otherDrv: Option[(ManagedEnvironment, api.driver.SidedBlock)] = None
private var blockData: Option[BlockData] = None
override val canUpdate = true
private final lazy val deviceInfo = Map(
DeviceAttribute.Class -> DeviceClass.Bus,
DeviceAttribute.Description -> "Remote Adapter",
DeviceAttribute.Vendor -> Constants.DeviceInfo.Scummtech,
DeviceAttribute.Product -> "ERR NAME NOT FOUND"
)
override def getDeviceInfo: util.Map[String, String] = deviceInfo
private def otherNode(tile: TileEntity, f: (Node) => Unit) {
network.Network.getNetworkNode(tile, dir) match {
case Some(otherNode) => f(otherNode)
case _ => // Nothing to do here
}
}
private def updateBoundState() {
if (node != null && node.network != null && coord.world.exists(_.provider.dimensionId == host.world.provider.dimensionId)
&& coord.toVec3.distanceTo(Vec3.createVectorHelper(host.xPosition, host.yPosition, host.zPosition)) <= Settings.get.mfuRange) {
host.world.getTileEntity(coord) match {
case env: TileEntity with api.network.Environment =>
otherEnv match {
case Some(environment: TileEntity) =>
otherNode(environment, node.disconnect)
otherEnv = None
case _ => // Nothing to do here.
}
otherEnv = Some(env)
// Remove any driver that might be there.
otherDrv match {
case Some((environment, driver)) =>
node.disconnect(environment.node)
environment.save(blockData.get.data)
Option(environment.node).foreach(_.remove())
otherDrv = None
case _ => // Nothing to do here.
}
otherNode(env, node.connect)
case _ =>
// Remove any environment that might have been there.
otherEnv match {
case Some(environment: TileEntity) =>
otherNode(environment, node.disconnect)
otherEnv = None
case _ => // Nothing to do here.
}
val (world, x, y, z) = (coord.world.get, coord.x, coord.y, coord.z)
Option(api.Driver.driverFor(world, coord.x, coord.y, coord.z, dir)) match {
case Some(newDriver) =>
otherDrv match {
case Some((oldEnvironment, driver)) =>
if (newDriver != driver) {
// This is... odd. Maybe moved by some other mod? First, clean up.
otherDrv = None
blockData = None
node.disconnect(oldEnvironment.node)
// Then rebuild - if we have something.
val environment = newDriver.createEnvironment(world, x, y, z, dir)
if (environment != null) {
otherDrv = Some((environment, newDriver))
blockData = 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, dir)
if (environment != null) {
otherDrv = Some((environment, newDriver))
blockData match {
case Some(data) if data.name == environment.getClass.getName =>
environment.load(data.data)
case _ =>
}
blockData = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
node.connect(environment.node)
}
}
case _ => otherDrv match {
case Some((environment, driver)) =>
// We had something there, but it's gone now...
node.disconnect(environment.node)
environment.save(blockData.get.data)
Option(environment.node).foreach(_.remove())
otherDrv = None
case _ => // Nothing before, nothing now.
}
}
}
}
}
private def disconnect() {
otherEnv match {
case Some(environment: TileEntity) =>
otherNode(environment, node.disconnect)
otherEnv = None
case _ => // Nothing to do here.
}
otherDrv match {
case Some((environment, driver)) =>
node.disconnect(environment.node)
environment.save(blockData.get.data)
Option(environment.node).foreach(_.remove())
otherDrv = None
case _ => // Nothing to do here.
}
}
override def onBlockChanged() = updateBoundState()
override def update() {
super.update()
otherDrv match {
case Some((env, drv)) if env.canUpdate => env.update()
case _ => // No driver
}
if (host.world.getTotalWorldTime % Settings.get.tickFrequency == 0) {
if (!node.tryChangeBuffer(-Settings.get.mfuCost * Settings.get.tickFrequency
* coord.toVec3.distanceTo(Vec3.createVectorHelper(host.xPosition, host.yPosition, host.zPosition)))) {
disconnect()
}
}
}
override def onConnect(node: Node) {
super.onConnect(node)
if (node == this.node) {
// Not checking for range yet because host may be a moving adapter, who knows?
BlockChangeHandler.addListener(this, coord)
updateBoundState()
}
}
override def onDisconnect(node: Node) {
super.onDisconnect(node)
otherEnv match {
case Some(env: TileEntity) => otherNode(env, (otherNode) => if (node == otherNode) otherEnv = None)
case _ => // No environment
}
otherDrv match {
case Some((env, drv)) if node == env.node => otherDrv = None
case _ => // No driver
}
if (node == this.node) {
BlockChangeHandler.removeListener(this)
}
}
override def load(nbt: NBTTagCompound) {
super.load(nbt)
Option(nbt.getCompoundTag(Settings.namespace + "adapter.block")) match {
case Some(blockNbt: NBTTagCompound) =>
if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) {
blockData = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data")))
}
case _ => // Invalid tag
}
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
val blockNbt = new NBTTagCompound()
blockData.foreach({ data =>
otherDrv.foreach(_._1.save(data.data))
blockNbt.setString("name", data.name)
blockNbt.setTag("data", data.data)
})
nbt.setTag(Settings.namespace + "adapter.block", blockNbt)
}
// ----------------------------------------------------------------------- //
private class BlockData(val name: String, val data: NBTTagCompound)
}