| package li.cil.oc.common.item |
| |
| import java.util |
| import java.util.UUID |
| import java.util.concurrent.{Callable, TimeUnit} |
| |
| import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotification} |
| import cpw.mods.fml.common.{ITickHandler, TickType} |
| import cpw.mods.fml.relauncher.{Side, SideOnly} |
| import li.cil.oc.api.driver.Container |
| import li.cil.oc.api.machine.Owner |
| import li.cil.oc.api.network.{Connector, Message, Node} |
| import li.cil.oc.api.{Machine, Rotatable} |
| import li.cil.oc.common.GuiType |
| import li.cil.oc.common.inventory.ComponentInventory |
| import li.cil.oc.util.ItemUtils.TabletData |
| import li.cil.oc.util.RotationHelper |
| import li.cil.oc.{OpenComputers, Settings, api} |
| import net.minecraft.entity.Entity |
| import net.minecraft.entity.player.EntityPlayer |
| import net.minecraft.item.ItemStack |
| import net.minecraft.nbt.{NBTTagCompound, NBTTagInt} |
| import net.minecraft.world.World |
| import net.minecraftforge.common.ForgeDirection |
| import net.minecraftforge.event.ForgeSubscribe |
| import net.minecraftforge.event.world.WorldEvent |
| |
| class Tablet(val parent: Delegator) extends Delegate { |
| showInItemList = false |
| |
| // Must be assembled to be usable. |
| |
| override def maxStackSize = 1 |
| |
| private var iconOn: Option[Icon] = None |
| private var iconOff: Option[Icon] = None |
| |
| @SideOnly(Side.CLIENT) |
| override def icon(stack: ItemStack, pass: Int) = Tablet.Client.get(stack) match { |
| case Some(wrapper) => if (wrapper.isRunning) iconOn else iconOff |
| case _ => super.icon(stack, pass) |
| } |
| |
| override def registerIcons(iconRegister: IconRegister) = { |
| super.registerIcons(iconRegister) |
| |
| iconOn = Option(iconRegister.registerIcon(Settings.resourceDomain + ":TabletOn")) |
| iconOff = Option(iconRegister.registerIcon(Settings.resourceDomain + ":TabletOff")) |
| } |
| |
| override def update(stack: ItemStack, world: World, player: Entity, slot: Int, selected: Boolean) = |
| Tablet.get(stack, player).update(world, player, slot, selected) |
| |
| override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = { |
| if (!player.isSneaking) { |
| if (world.isRemote) { |
| player.openGui(OpenComputers, GuiType.Tablet.id, world, 0, 0, 0) |
| } |
| else { |
| Tablet.get(stack, player).start() |
| } |
| } |
| else { |
| if (world.isRemote) Tablet.Client.remove(stack) |
| else Tablet.Server.remove(stack) |
| } |
| player.swingItem() |
| stack |
| } |
| } |
| |
| class TabletWrapper(var stack: ItemStack, var holder: Entity) extends ComponentInventory with Container with Owner with Rotatable { |
| lazy val computer = if (holder.worldObj.isRemote) null else Machine.create(this) |
| |
| val data = new TabletData() |
| |
| def items = data.items |
| |
| override def facing = RotationHelper.fromYaw(holder.rotationYaw) |
| |
| override def toLocal(value: ForgeDirection) = value // TODO do we care? |
| |
| override def toGlobal(value: ForgeDirection) = value // TODO do we care? |
| |
| def readFromNBT() { |
| if (stack.hasTagCompound) { |
| val data = stack.getTagCompound |
| if (!world.isRemote) { |
| computer.load(data.getCompoundTag(Settings.namespace + "data")) |
| } |
| load(data) |
| } |
| } |
| |
| def writeToNBT() { |
| if (!stack.hasTagCompound) { |
| stack.setTagCompound(new NBTTagCompound("tag")) |
| } |
| val data = stack.getTagCompound |
| if (!world.isRemote) { |
| if (!data.hasKey(Settings.namespace + "data")) { |
| data.setTag(Settings.namespace + "data", new NBTTagCompound()) |
| } |
| computer.save(data.getCompoundTag(Settings.namespace + "data")) |
| } |
| save(data) |
| } |
| |
| readFromNBT() |
| if (world.isRemote) { |
| connectComponents() |
| components collect { |
| case Some(buffer: api.component.TextBuffer) => |
| buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit) |
| buffer.setMaximumResolution(80, 25) |
| } |
| } |
| else { |
| api.Network.joinNewNetwork(computer.node) |
| writeToNBT() |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def onConnect(node: Node) { |
| if (node == this.node) { |
| connectComponents() |
| } |
| else node.host match { |
| case buffer: api.component.TextBuffer => |
| buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit) |
| buffer.setMaximumResolution(80, 25) |
| case _ => |
| } |
| } |
| |
| override protected def connectItemNode(node: Node) { |
| super.connectItemNode(node) |
| if (node != null) node.host match { |
| case buffer: api.component.TextBuffer => components collect { |
| case Some(keyboard: api.component.Keyboard) => buffer.node.connect(keyboard.node) |
| } |
| case keyboard: api.component.Keyboard => components collect { |
| case Some(buffer: api.component.TextBuffer) => keyboard.node.connect(buffer.node) |
| } |
| case _ => |
| } |
| } |
| |
| override def onDisconnect(node: Node) { |
| if (node == this.node) { |
| disconnectComponents() |
| } |
| } |
| |
| override def onMessage(message: Message) {} |
| |
| override def componentContainer = this |
| |
| override def getSizeInventory = items.length |
| |
| override def isItemValidForSlot(slot: Int, stack: ItemStack) = true |
| |
| override def isUseableByPlayer(player: EntityPlayer) = canInteract(player.getCommandSenderName) |
| |
| override def onInventoryChanged() {} |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def xPosition = holder.posX |
| |
| override def yPosition = holder.posY + 1 |
| |
| override def zPosition = holder.posZ |
| |
| override def markChanged() {} |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def x = holder.posX.toInt |
| |
| override def y = holder.posY.toInt + 1 |
| |
| override def z = holder.posZ.toInt |
| |
| override def world = holder.worldObj |
| |
| override def installedMemory = items.foldLeft(0)((acc, itemOption) => acc + (itemOption match { |
| case Some(item) => Option(api.Driver.driverFor(item)) match { |
| case Some(driver: api.driver.Memory) => driver.amount(item) |
| case _ => 0 |
| } |
| case _ => 0 |
| })) |
| |
| override def maxComponents = items.foldLeft(0)((acc, itemOption) => acc + (itemOption match { |
| case Some(item) => Option(api.Driver.driverFor(item)) match { |
| case Some(driver: api.driver.Processor) => driver.supportedComponents(item) |
| case _ => 0 |
| } |
| case _ => 0 |
| })) |
| |
| override def markAsChanged() {} |
| |
| override def onMachineConnect(node: Node) = onConnect(node) |
| |
| override def onMachineDisconnect(node: Node) = onDisconnect(node) |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def node = Option(computer).fold(null: Node)(_.node) |
| |
| override def canInteract(player: String) = computer.canInteract(player) |
| |
| override def isRunning = if (world.isRemote) { |
| import li.cil.oc.util.ExtendedNBT._ |
| val computerData = stack.getTagCompound.getCompoundTag(Settings.namespace + "data") |
| val state = computerData.getTagList("state").iterator[NBTTagInt].headOption.fold(0)(_.data) |
| state != 0 |
| } |
| else computer.isRunning |
| |
| override def isPaused = computer.isPaused |
| |
| override def start() = computer.start() |
| |
| override def pause(seconds: Double) = computer.pause(seconds) |
| |
| override def stop() = computer.stop() |
| |
| override def signal(name: String, args: AnyRef*) = computer.signal(name, args) |
| |
| // ----------------------------------------------------------------------- // |
| |
| def update(world: World, player: Entity, slot: Int, selected: Boolean) { |
| holder = player |
| if (!world.isRemote) { |
| computer.node.asInstanceOf[Connector].changeBuffer(500) |
| computer.update() |
| updateComponents() |
| } |
| } |
| |
| // ----------------------------------------------------------------------- // |
| |
| override def load(nbt: NBTTagCompound) { |
| data.load(nbt) |
| } |
| |
| override def save(nbt: NBTTagCompound) { |
| saveComponents() |
| data.save(nbt) |
| } |
| } |
| |
| object Tablet extends ITickHandler { |
| def get(stack: ItemStack, holder: Entity) = { |
| if (holder.worldObj.isRemote) Client.get(stack, holder) |
| else Server.get(stack, holder) |
| } |
| |
| @ForgeSubscribe |
| def onWorldSave(e: WorldEvent.Save) { |
| Server.saveAll(e.world) |
| } |
| |
| @ForgeSubscribe |
| def onWorldUnload(e: WorldEvent.Unload) { |
| Client.clear() |
| Server.clear() |
| } |
| |
| override def getLabel = "OpenComputers Tablet Cleanup Ticker" |
| |
| override def ticks = util.EnumSet.of(TickType.CLIENT, TickType.SERVER) |
| |
| override def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) { |
| if (tickType.contains(TickType.CLIENT)) Client.cleanUp() |
| if (tickType.contains(TickType.SERVER)) Server.cleanUp() |
| } |
| |
| override def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {} |
| |
| abstract class Cache extends Callable[TabletWrapper] with RemovalListener[String, TabletWrapper] { |
| val cache = com.google.common.cache.CacheBuilder.newBuilder(). |
| expireAfterAccess(10, TimeUnit.SECONDS). |
| removalListener(this). |
| asInstanceOf[CacheBuilder[String, TabletWrapper]]. |
| build[String, TabletWrapper]() |
| |
| // To allow access in cache entry init. |
| private var currentStack: ItemStack = _ |
| |
| private var currentHolder: Entity = _ |
| |
| def get(stack: ItemStack, holder: Entity) = { |
| if (!stack.hasTagCompound) { |
| stack.setTagCompound(new NBTTagCompound()) |
| } |
| if (!stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { |
| stack.getTagCompound.setString(Settings.namespace + "tablet", UUID.randomUUID().toString) |
| } |
| val id = stack.getTagCompound.getString(Settings.namespace + "tablet") |
| cache.synchronized { |
| currentStack = stack |
| currentHolder = holder |
| val wrapper = cache.get(id, this) |
| wrapper.stack = stack |
| wrapper.holder = holder |
| wrapper |
| } |
| } |
| |
| def call() = new TabletWrapper(currentStack, currentHolder) |
| |
| def onRemoval(e: RemovalNotification[String, TabletWrapper]) { |
| val tablet = e.getValue |
| if (tablet.node != null) { |
| // Server. |
| tablet.writeToNBT() |
| tablet.stop() |
| tablet.node.remove() |
| } |
| } |
| |
| def remove(stack: ItemStack) { |
| cache.synchronized { |
| cache.invalidate(stack) |
| cache.cleanUp() |
| } |
| } |
| |
| def clear() { |
| cache.synchronized { |
| cache.invalidateAll() |
| cache.cleanUp() |
| } |
| } |
| |
| def cleanUp() { |
| cache.synchronized(cache.cleanUp()) |
| } |
| } |
| |
| object Client extends Cache { |
| def get(stack: ItemStack) = { |
| if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "tablet")) { |
| val id = stack.getTagCompound.getString(Settings.namespace + "tablet") |
| cache.synchronized(Option(cache.getIfPresent(id))) |
| } |
| else None |
| } |
| } |
| |
| object Server extends Cache { |
| def saveAll(world: World) { |
| cache.synchronized { |
| import scala.collection.convert.WrapAsScala._ |
| for (tablet <- cache.asMap.values if tablet.world == world) { |
| tablet.writeToNBT() |
| } |
| } |
| } |
| } |
| |
| } |