|  | 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.Machine | 
|  | 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.common.GuiType | 
|  | import li.cil.oc.common.inventory.ComponentInventory | 
|  | import li.cil.oc.util.ItemUtils.TabletData | 
|  | 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.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 { | 
|  | lazy val computer = if (holder.worldObj.isRemote) null else Machine.create(this) | 
|  |  | 
|  | val data = new TabletData() | 
|  |  | 
|  | def items = data.items | 
|  |  | 
|  | 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() | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } |