diff --git a/src/main/java/li/cil/oc/api/machine/Context.java b/src/main/java/li/cil/oc/api/machine/Context.java
index 622696e2a..fe82dacb6 100644
--- a/src/main/java/li/cil/oc/api/machine/Context.java
+++ b/src/main/java/li/cil/oc/api/machine/Context.java
@@ -153,6 +153,7 @@ public interface Context {
*
Strings.
* Byte arrays (which appear as strings on the Lua side, e.g.).
* Maps if and only if both keys and values are strings.
+ * NBTTagCompounds.
*
* If an unsupported type is specified the method will enqueue nothing
* instead, resulting in a nil on the Lua side, e.g., and log a
diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala
index 59590a8df..22a3a9efd 100644
--- a/src/main/scala/li/cil/oc/client/PacketSender.scala
+++ b/src/main/scala/li/cil/oc/client/PacketSender.scala
@@ -110,6 +110,15 @@ object PacketSender {
pb.sendToServer()
}
+ def sendCopyToAnalyzer(address: String, line: Int): Unit = {
+ val pb = new SimplePacketBuilder(PacketType.CopyToAnalyzer)
+
+ pb.writeUTF(address)
+ pb.writeInt(line)
+
+ pb.sendToServer()
+ }
+
def sendMultiPlace() {
val pb = new SimplePacketBuilder(PacketType.MultiPartPlace)
pb.sendToServer()
diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
index 5384f8952..c026f22af 100644
--- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
+++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
@@ -284,7 +284,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
val timeJitter = robot.hashCode ^ 0xFF
val hover =
- if (robot.isRunning) (Math.sin(timeJitter + (worldTime + f) / 20.0) * 0.03).toFloat
+ if (robot.isRunning) (Math.sin(timeJitter + worldTime / 20.0) * 0.03).toFloat
else -0.03f
GL11.glTranslatef(0, hover, 0)
diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala
index b1419570f..b4f535e07 100644
--- a/src/main/scala/li/cil/oc/common/EventHandler.scala
+++ b/src/main/scala/li/cil/oc/common/EventHandler.scala
@@ -19,6 +19,7 @@ import net.minecraft.item.ItemStack
import net.minecraft.server.MinecraftServer
import net.minecraft.tileentity.TileEntity
import net.minecraftforge.common.util.FakePlayer
+import net.minecraftforge.event.world.BlockEvent
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.PlayerEvent._
@@ -114,6 +115,22 @@ object EventHandler {
}
}
+ @SubscribeEvent
+ def onBlockBreak(e: BlockEvent.BreakEvent): Unit = {
+ e.world.getTileEntity(e.pos) match {
+ case c: tileentity.Case =>
+ if (c.isCreative && (!e.getPlayer.capabilities.isCreativeMode || !c.canInteract(e.getPlayer.getName))) {
+ e.setCanceled(true)
+ }
+ case r: tileentity.RobotProxy =>
+ val robot = r.robot
+ if (robot.isCreative && (!e.getPlayer.capabilities.isCreativeMode || !robot.canInteract(e.getPlayer.getName))) {
+ e.setCanceled(true)
+ }
+ case _ =>
+ }
+ }
+
lazy val drone = api.Items.get("drone")
lazy val eeprom = api.Items.get("eeprom")
lazy val mcu = api.Items.get("microcontroller")
diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala
index ee8251214..448070068 100644
--- a/src/main/scala/li/cil/oc/common/PacketType.scala
+++ b/src/main/scala/li/cil/oc/common/PacketType.scala
@@ -51,6 +51,7 @@ object PacketType extends Enumeration {
// Client -> Server
ComputerPower,
+ CopyToAnalyzer,
DronePower,
KeyDown,
KeyUp,
diff --git a/src/main/scala/li/cil/oc/common/block/Case.scala b/src/main/scala/li/cil/oc/common/block/Case.scala
index 4042703aa..ca8bd32e5 100644
--- a/src/main/scala/li/cil/oc/common/block/Case.scala
+++ b/src/main/scala/li/cil/oc/common/block/Case.scala
@@ -86,7 +86,9 @@ class Case(val tier: Int) extends RedstoneAware with traits.PowerAcceptor with t
override def removedByPlayer(world: World, pos: BlockPos, player: EntityPlayer, willHarvest: Boolean) =
world.getTileEntity(pos) match {
- case c: tileentity.Case => c.canInteract(player.getName) && super.removedByPlayer(world, pos, player, willHarvest)
+ case c: tileentity.Case =>
+ if (c.isCreative && (!player.capabilities.isCreativeMode || !c.canInteract(player.getName))) false
+ else c.canInteract(player.getName) && super.removedByPlayer(world, pos, player, willHarvest)
case _ => super.removedByPlayer(world, pos, player, willHarvest)
}
}
diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala
index bd10b19f2..dacf057cc 100644
--- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala
+++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala
@@ -230,6 +230,11 @@ class RobotProxy extends RedstoneAware with traits.StateAware {
world.getTileEntity(pos) match {
case proxy: tileentity.RobotProxy =>
val robot = proxy.robot
+ // Only allow breaking creative tier robots by allowed users.
+ // Unlike normal robots, griefing isn't really a valid concern
+ // here, because to get a creative robot you need creative
+ // mode in the first place.
+ if (robot.isCreative && (!player.capabilities.isCreativeMode || !robot.canInteract(player.getName))) return false
if (!world.isRemote) {
if (robot.player == player) return false
robot.node.remove()
diff --git a/src/main/scala/li/cil/oc/common/block/Screen.scala b/src/main/scala/li/cil/oc/common/block/Screen.scala
index b2b3656d1..6a4bb29b4 100644
--- a/src/main/scala/li/cil/oc/common/block/Screen.scala
+++ b/src/main/scala/li/cil/oc/common/block/Screen.scala
@@ -4,6 +4,7 @@ import java.util
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
+import li.cil.oc.api
import li.cil.oc.common.GuiType
import li.cil.oc.common.tileentity
import li.cil.oc.integration.util.Wrench
@@ -13,6 +14,7 @@ import li.cil.oc.util.Rarity
import li.cil.oc.util.Tooltip
import net.minecraft.block.properties.IProperty
import net.minecraft.block.state.IBlockState
+import net.minecraft.client.Minecraft
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.player.EntityPlayer
@@ -88,6 +90,7 @@ class Screen(val tier: Int) extends RedstoneAware with traits.OmniRotatable {
def rightClick(world: World, pos: BlockPos, player: EntityPlayer,
side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float, force: Boolean) = {
if (Wrench.holdsApplicableWrench(player, pos) && getValidRotations(world, pos).contains(side) && !force) false
+ else if (api.Items.get(player.getHeldItem) == api.Items.get("analyzer")) false
else world.getTileEntity(pos) match {
case screen: tileentity.Screen if screen.hasKeyboard && (force || player.isSneaking == screen.invertTouchMode) =>
// Yep, this GUI is actually purely client side. We could skip this
@@ -98,7 +101,10 @@ class Screen(val tier: Int) extends RedstoneAware with traits.OmniRotatable {
}
true
case screen: tileentity.Screen if screen.tier > 0 && side == screen.facing =>
- screen.click(player, hitX, hitY, hitZ)
+ if (world.isRemote && player == Minecraft.getMinecraft.thePlayer) {
+ screen.click(hitX, hitY, hitZ)
+ }
+ else true
case _ => false
}
}
diff --git a/src/main/scala/li/cil/oc/common/component/Terminal.scala b/src/main/scala/li/cil/oc/common/component/Terminal.scala
index e2f812b64..fc8c0c862 100644
--- a/src/main/scala/li/cil/oc/common/component/Terminal.scala
+++ b/src/main/scala/li/cil/oc/common/component/Terminal.scala
@@ -6,6 +6,7 @@ import li.cil.oc.api.component.Keyboard.UsabilityChecker
import li.cil.oc.api.network.Component
import li.cil.oc.api.network.Node
import li.cil.oc.api.network.Visibility
+import li.cil.oc.common.Tier
import li.cil.oc.common.item
import li.cil.oc.common.item.Delegator
import li.cil.oc.common.tileentity
@@ -23,9 +24,9 @@ class Terminal(val rack: tileentity.ServerRack, val number: Int) {
val buffer = {
val screenItem = api.Items.get("screen1").createItemStack(1)
val buffer = api.Driver.driverFor(screenItem, rack.getClass).createEnvironment(screenItem, rack).asInstanceOf[api.component.TextBuffer]
- val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(1)
+ val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(Tier.Three)
buffer.setMaximumResolution(maxWidth, maxHeight)
- buffer.setMaximumColorDepth(Settings.screenDepthsByTier(1))
+ buffer.setMaximumColorDepth(Settings.screenDepthsByTier(Tier.Three))
buffer
}
diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala
index d7a660e98..56d50ba9f 100644
--- a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala
+++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala
@@ -415,6 +415,10 @@ class TextBuffer(val host: EnvironmentHost) extends prefab.ManagedEnvironment wi
override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer) =
proxy.mouseScroll(x, y, delta, player)
+ def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
+ proxy.copyToAnalyzer(line, player)
+ }
+
// ----------------------------------------------------------------------- //
override def onConnect(node: Node) {
@@ -588,6 +592,8 @@ object TextBuffer {
def mouseUp(x: Double, y: Double, button: Int, player: EntityPlayer): Unit
def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit
+
+ def copyToAnalyzer(line: Int, player: EntityPlayer): Unit
}
class ClientProxy(val owner: TextBuffer) extends Proxy {
@@ -672,6 +678,10 @@ object TextBuffer {
ClientPacketSender.sendMouseScroll(nodeAddress, x, y, delta)
}
+ override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
+ ClientPacketSender.sendCopyToAnalyzer(nodeAddress, line)
+ }
+
private lazy val Debugger = api.Items.get("debugger")
private def debug(message: String) {
@@ -775,6 +785,27 @@ object TextBuffer {
sendMouseEvent(player, "scroll", x, y, delta)
}
+ override def copyToAnalyzer(line: Int, player: EntityPlayer): Unit = {
+ val stack = player.getHeldItem
+ if (stack != null) {
+ if (!stack.hasTagCompound) {
+ stack.setTagCompound(new NBTTagCompound())
+ }
+ stack.getTagCompound.removeTag(Settings.namespace + "clipboard")
+
+ if (line >= 0 && line < owner.data.height) {
+ val text = new String(owner.data.buffer(line)).trim
+ if (!Strings.isNullOrEmpty(text)) {
+ stack.getTagCompound.setString(Settings.namespace + "clipboard", text)
+ }
+ }
+
+ if (stack.getTagCompound.hasNoTags) {
+ stack.setTagCompound(null)
+ }
+ }
+ }
+
private def sendMouseEvent(player: EntityPlayer, name: String, x: Double, y: Double, data: Int) = {
val args = mutable.ArrayBuffer.empty[AnyRef]
diff --git a/src/main/scala/li/cil/oc/common/item/Analyzer.scala b/src/main/scala/li/cil/oc/common/item/Analyzer.scala
index e762790ec..d54f088eb 100644
--- a/src/main/scala/li/cil/oc/common/item/Analyzer.scala
+++ b/src/main/scala/li/cil/oc/common/item/Analyzer.scala
@@ -1,9 +1,11 @@
package li.cil.oc.common.item
import li.cil.oc.Localization
+import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.network.Analyzable
import li.cil.oc.api.network._
+import li.cil.oc.common.tileentity
import li.cil.oc.server.PacketSender
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedWorld._
@@ -11,6 +13,7 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing
+import net.minecraft.world.World
import net.minecraftforge.event.entity.player.EntityInteractEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -79,7 +82,31 @@ object Analyzer {
}
class Analyzer(val parent: Delegator) extends Delegate {
+ override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
+ if (player.isSneaking && stack.hasTagCompound) {
+ stack.getTagCompound.removeTag(Settings.namespace + "clipboard")
+ if (stack.getTagCompound.hasNoTags) {
+ stack.setTagCompound(null)
+ }
+ }
+ super.onItemRightClick(stack, world, player)
+ }
+
override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = {
- Analyzer.analyze(position.world.get.getTileEntity(position), player, side, hitX, hitY, hitZ)
+ val world = player.getEntityWorld
+ world.getTileEntity(position) match {
+ case screen: tileentity.Screen if side == screen.facing =>
+ if (player.isSneaking) {
+ screen.copyToAnalyzer(hitX, hitY, hitZ)
+ }
+ else if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "clipboard")) {
+ if (!world.isRemote) {
+ screen.origin.buffer.clipboard(stack.getTagCompound.getString(Settings.namespace + "clipboard"), player)
+ }
+ true
+ }
+ else false
+ case _ => Analyzer.analyze(position.world.get.getTileEntity(position), player, side, hitX, hitY, hitZ)
+ }
}
}
diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala
index bb63133b5..7f6adeb55 100644
--- a/src/main/scala/li/cil/oc/common/item/Tablet.scala
+++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala
@@ -29,6 +29,7 @@ import li.cil.oc.common.inventory.ComponentInventory
import li.cil.oc.common.item.data.TabletData
import li.cil.oc.integration.opencomputers.DriverScreen
import li.cil.oc.server.component
+import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.Rarity
import li.cil.oc.util.RotationHelper
@@ -93,6 +94,24 @@ class Tablet(val parent: Delegator) extends Delegate {
case _ =>
}
+ override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Boolean = {
+ val world = player.getEntityWorld
+ if (!world.isRemote) try {
+ val computer = Tablet.get(stack, player).machine
+ if (computer.isRunning) {
+ val data = new NBTTagCompound()
+ computer.node.sendToReachable("tablet.use", data, stack, player, position, side, float2Float(hitX), float2Float(hitY), float2Float(hitZ))
+ if (!data.hasNoTags) {
+ computer.signal("tablet_use", data)
+ }
+ }
+ }
+ catch {
+ case t: Throwable => OpenComputers.log.warn("Block analysis on tablet right click failed gloriously!", t)
+ }
+ true
+ }
+
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = {
if (!player.isSneaking) {
if (world.isRemote) {
diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala
index 72b010f18..d0a57e8d2 100644
--- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala
+++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala
@@ -35,7 +35,7 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with
var maxComponents = 0
- private def isCreativeCase = tier == Tier.Four
+ def isCreative = tier == Tier.Four
// ----------------------------------------------------------------------- //
@@ -74,7 +74,7 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with
override def canUpdate = isServer
override def updateEntity() {
- if (isServer && isCreativeCase && world.getTotalWorldTime % Settings.get.tickFrequency == 0) {
+ if (isServer && isCreative && world.getTotalWorldTime % Settings.get.tickFrequency == 0) {
// Creative case, make it generate power.
node.asInstanceOf[Connector].changeBuffer(Double.PositiveInfinity)
}
@@ -135,7 +135,7 @@ class Case(var tier: Int) extends traits.PowerAcceptor with traits.Computer with
override def getSizeInventory = if (tier < 0 || tier >= InventorySlots.computer.length) 0 else InventorySlots.computer(tier).length
override def isUseableByPlayer(player: EntityPlayer) =
- super.isUseableByPlayer(player) && (!isCreativeCase || player.capabilities.isCreativeMode)
+ super.isUseableByPlayer(player) && (!isCreative || player.capabilities.isCreativeMode)
override def isItemValidForSlot(slot: Int, stack: ItemStack) =
Option(Driver.driverFor(stack, getClass)).fold(false)(driver => {
diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala
index 6c0b69b49..ce97a8340 100644
--- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala
+++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala
@@ -70,6 +70,8 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot
override def tier = info.tier
+ def isCreative = tier == Tier.Four
+
// Wrapper for the part of the inventory that is mutable.
val dynamicInventory = new IInventory {
override def getSizeInventory = Robot.this.inventorySize
@@ -749,6 +751,9 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot
else if (stack != null && stack.stackSize > 0) spawnStackInWorld(stack, Option(EnumFacing.UP))
}
+ override def isUseableByPlayer(player: EntityPlayer) =
+ super.isUseableByPlayer(player) && (!isCreative || player.capabilities.isCreativeMode)
+
override def isItemValidForSlot(slot: Int, stack: ItemStack) = (slot, Option(Driver.driverFor(stack, getClass))) match {
case (0, _) => true // Allow anything in the tool slot.
case (i, Some(driver)) if isContainerSlot(i) =>
diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala
index 1db36b52f..b3308b14d 100644
--- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala
+++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala
@@ -3,6 +3,7 @@ package li.cil.oc.common.tileentity
import li.cil.oc.Settings
import li.cil.oc.api.network.Analyzable
import li.cil.oc.api.network._
+import li.cil.oc.common.component.TextBuffer
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.Color
import li.cil.oc.util.ExtendedWorld._
@@ -95,7 +96,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
invertTouchMode = false
}
- def click(player: EntityPlayer, hitX: Double, hitY: Double, hitZ: Double): Boolean = {
+ def toScreenCoordinates(hitX: Double, hitY: Double, hitZ: Double): (Boolean, Option[(Double, Double)]) = {
// Compute absolute position of the click on the face, measured in blocks.
def dot(f: EnumFacing) = f.getFrontOffsetX * hitX + f.getFrontOffsetY * hitY + f.getFrontOffsetZ * hitZ
val (hx, hy) = (dot(toGlobal(EnumFacing.EAST)), dot(toGlobal(EnumFacing.UP)))
@@ -107,9 +108,9 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
// Get the relative position in the *display area* of the face.
val border = 2.25 / 16.0
if (ax <= border || ay <= border || ax >= width - border || ay >= height - border) {
- return false
+ return (false, None)
}
- if (!world.isRemote) return true
+ if (!world.isRemote) return (true, None)
val (iw, ih) = (width - border * 2, height - border * 2)
val (rx, ry) = ((ax - border) / iw, (ay - border) / ih)
@@ -121,27 +122,43 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
val (brx, bry) = if (bpw > bph) {
val rh = bph.toDouble / bpw.toDouble
val bry = (ry - (1 - rh) * 0.5) / rh
- if (bry <= 0 || bry >= 1) {
- return true
- }
(rx, bry)
}
else if (bph > bpw) {
val rw = bpw.toDouble / bph.toDouble
val brx = (rx - (1 - rw) * 0.5) / rw
- if (brx <= 0 || brx >= 1) {
- return true
- }
(brx, ry)
}
else {
(rx, ry)
}
- // Convert to absolute coordinates and send the packet to the server.
- origin.buffer.mouseDown(brx * bw, bry * bh, 0, null)
+ val inBounds = bry >= 0 && bry <= 1 && brx >= 0 || brx <= 1
+ (inBounds, Some((brx * bw, bry * bh)))
+ }
- true
+ def copyToAnalyzer(hitX: Double, hitY: Double, hitZ: Double): Boolean = {
+ val (inBounds, coordinates) = toScreenCoordinates(hitX, hitY, hitZ)
+ coordinates match {
+ case Some((x, y)) => origin.buffer match {
+ case buffer: TextBuffer =>
+ buffer.copyToAnalyzer(y.toInt, null)
+ true
+ case _ => false
+ }
+ case _ => inBounds
+ }
+ }
+
+ def click(hitX: Double, hitY: Double, hitZ: Double): Boolean = {
+ val (inBounds, coordinates) = toScreenCoordinates(hitX, hitY, hitZ)
+ coordinates match {
+ case Some((x, y)) =>
+ // Send the packet to the server (manually, for accuracy).
+ origin.buffer.mouseDown(x, y, 0, null)
+ true
+ case _ => inBounds
+ }
}
def walk(entity: Entity) {
@@ -238,7 +255,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
hitXInner && !hitYInner && hitZInner ||
!hitXInner && hitYInner && hitZInner) {
arrow.shootingEntity match {
- case player: EntityPlayer if player == Minecraft.getMinecraft.thePlayer => click(player, hitX, hitY, hitZ)
+ case player: EntityPlayer if player == Minecraft.getMinecraft.thePlayer => click(hitX, hitY, hitZ)
case _ =>
}
}
diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ConverterNBT.scala b/src/main/scala/li/cil/oc/integration/vanilla/ConverterNBT.scala
new file mode 100644
index 000000000..056b0702b
--- /dev/null
+++ b/src/main/scala/li/cil/oc/integration/vanilla/ConverterNBT.scala
@@ -0,0 +1,35 @@
+package li.cil.oc.integration.vanilla
+
+import java.util
+
+import li.cil.oc.api
+import net.minecraft.nbt._
+
+import scala.collection.convert.WrapAsScala._
+
+object ConverterNBT extends api.driver.Converter {
+ override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) =
+ value match {
+ case nbt: NBTTagCompound => output += "oc:flatten" -> convert(nbt)
+ case _ =>
+ }
+
+ private def convert(nbt: NBTBase): AnyRef = nbt match {
+ case tag: NBTTagByte => byte2Byte(tag.getByte)
+ case tag: NBTTagShort => short2Short(tag.getShort)
+ case tag: NBTTagInt => int2Integer(tag.getInt)
+ case tag: NBTTagLong => long2Long(tag.getLong)
+ case tag: NBTTagFloat => float2Float(tag.getFloat)
+ case tag: NBTTagDouble => double2Double(tag.getDouble)
+ case tag: NBTTagByteArray => tag.getByteArray
+ case tag: NBTTagString => tag.getString
+ case tag: NBTTagList =>
+ val copy = tag.copy().asInstanceOf[NBTTagList]
+ (0 until copy.tagCount).map(_ => convert(copy.removeTag(0))).toArray
+ case tag: NBTTagCompound =>
+ tag.getKeySet.collect {
+ case key: String => key -> convert(tag.getTag(key))
+ }.toMap
+ case tag: NBTTagIntArray => tag.getIntArray
+ }
+}
diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala
index cf98113bf..f525cc064 100644
--- a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala
+++ b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala
@@ -31,5 +31,6 @@ object ModVanilla extends ModProxy {
Driver.add(ConverterFluidStack)
Driver.add(ConverterFluidTankInfo)
Driver.add(ConverterItemStack)
+ Driver.add(ConverterNBT)
}
}
diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala
index cddd4ef2b..4b6860891 100644
--- a/src/main/scala/li/cil/oc/server/PacketHandler.scala
+++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala
@@ -36,6 +36,7 @@ object PacketHandler extends CommonPacketHandler {
override def dispatch(p: PacketParser) {
p.packetType match {
case PacketType.ComputerPower => onComputerPower(p)
+ case PacketType.CopyToAnalyzer => onCopyToAnalyzer(p)
case PacketType.DronePower => onDronePower(p)
case PacketType.KeyDown => onKeyDown(p)
case PacketType.KeyUp => onKeyUp(p)
@@ -73,6 +74,13 @@ object PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet.
}
+ def onCopyToAnalyzer(p: PacketParser) {
+ ComponentTracker.get(p.player.worldObj, p.readUTF()) match {
+ case Some(buffer: TextBuffer) => buffer.copyToAnalyzer(p.readInt(), p.player.asInstanceOf[EntityPlayer])
+ case _ => // Invalid Packet
+ }
+ }
+
def onDronePower(p: PacketParser) =
p.readEntity[Drone]() match {
case Some(drone) => p.player match {
diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala
index da2e68985..6a7b3694d 100644
--- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala
+++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala
@@ -35,6 +35,9 @@ import net.minecraft.world.WorldServer
import net.minecraft.world.WorldSettings.GameType
import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.util.FakePlayerFactory
+import net.minecraftforge.fluids.FluidRegistry
+import net.minecraftforge.fluids.FluidStack
+import net.minecraftforge.fluids.IFluidHandler
class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Neighbors).
@@ -426,6 +429,7 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number, slot:number[, count:number]):number - Reduce the size of an item stack in the inventory at the specified location.""")
def removeItem(context: Context, args: Arguments): Array[AnyRef] = {
+ checkEnabled()
val position = BlockPosition(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2), world)
InventoryUtils.inventoryAt(position) match {
case Some(inventory) =>
@@ -438,6 +442,34 @@ object DebugCard {
}
}
+ @Callback(doc = """function(id:string, amount:number, x:number, y:number, z:number, side:number):boolean - Insert some fluid into the tank at the specified location.""")
+ def insertFluid(context: Context, args: Arguments): Array[AnyRef] = {
+ checkEnabled()
+ val fluid = FluidRegistry.getFluid(args.checkString(0))
+ if (fluid == null) {
+ throw new IllegalArgumentException("invalid fluid id")
+ }
+ val amount = args.checkInteger(1)
+ val position = BlockPosition(args.checkDouble(2), args.checkDouble(3), args.checkDouble(4), world)
+ val side = args.checkSide(5, EnumFacing.values: _*)
+ world.getTileEntity(position) match {
+ case handler: IFluidHandler => result(handler.fill(side, new FluidStack(fluid, amount), true))
+ case _ => result(null, "no tank")
+ }
+ }
+
+ @Callback(doc = """function(amount:number, x:number, y:number, z:number, side:number):boolean - Remove some fluid from a tank at the specified location.""")
+ def removeFluid(context: Context, args: Arguments): Array[AnyRef] = {
+ checkEnabled()
+ val amount = args.checkInteger(0)
+ val position = BlockPosition(args.checkDouble(1), args.checkDouble(2), args.checkDouble(3), world)
+ val side = args.checkSide(4, EnumFacing.values: _*)
+ world.getTileEntity(position) match {
+ case handler: IFluidHandler => result(handler.drain(side, amount, true))
+ case _ => result(null, "no tank")
+ }
+ }
+
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
diff --git a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala
index 07fde262f..4aa89311c 100644
--- a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala
+++ b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala
@@ -1,22 +1,27 @@
package li.cil.oc.server.component
+import com.google.common.base.Strings
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.event.GeolyzerEvent
import li.cil.oc.api.event.GeolyzerEvent.Analyze
-import li.cil.oc.api.internal.Rotatable
+import li.cil.oc.api.internal
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
+import li.cil.oc.api.network.Message
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._
+import net.minecraft.block.Block
+import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.EnumFacing
import net.minecraftforge.common.MinecraftForge
@@ -51,7 +56,7 @@ class Geolyzer(val host: EnvironmentHost) extends prefab.ManagedEnvironment {
def analyze(computer: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val side = args.checkSide(0, EnumFacing.values: _*)
val globalSide = host match {
- case rotatable: Rotatable => rotatable.toGlobal(side)
+ case rotatable: internal.Rotatable => rotatable.toGlobal(side)
case _ => side
}
val options = args.optTable(1, Map.empty[AnyRef, AnyRef])
@@ -70,7 +75,7 @@ class Geolyzer(val host: EnvironmentHost) extends prefab.ManagedEnvironment {
def store(computer: Context, args: Arguments): Array[AnyRef] = {
val side = args.checkSide(0, EnumFacing.values: _*)
val globalSide = host match {
- case rotatable: Rotatable => rotatable.toGlobal(side)
+ case rotatable: internal.Rotatable => rotatable.toGlobal(side)
case _ => side
}
@@ -93,4 +98,42 @@ class Geolyzer(val host: EnvironmentHost) extends prefab.ManagedEnvironment {
})
}
}
+
+ override def onMessage(message: Message): Unit = {
+ super.onMessage(message)
+ if (message.name == "tablet.use") message.source.host match {
+ case machine: api.machine.Machine => (machine.host, message.data) match {
+ case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) =>
+ if (node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) {
+ // TODO 1.5 replace with event (change event to allow arbitrary coordinates)
+ val world = player.getEntityWorld
+ val block = world.getBlock(blockPos)
+
+ Block.blockRegistry.getNameForObject(block) match {
+ case name: String if !Strings.isNullOrEmpty(name) => nbt.setString("name", name)
+ case _ =>
+ }
+ nbt.setInteger("metadata", block.getMetaFromState(world.getBlockMetadata(blockPos)))
+ nbt.setFloat("hardness", world.getBlockHardness(blockPos))
+ nbt.setInteger("harvestLevel", world.getBlockHarvestLevel(blockPos))
+ if (!Strings.isNullOrEmpty(world.getBlockHarvestTool(blockPos))) {
+ nbt.setString("harvestTool", world.getBlockHarvestTool(blockPos))
+ }
+ nbt.setInteger("color", world.getBlockMapColor(blockPos).colorValue)
+
+// val event = new Analyze(host, Map.empty[AnyRef, AnyRef], side)
+// MinecraftForge.EVENT_BUS.post(event)
+// if (!event.isCanceled) {
+// for ((key, value) <- event.data) value match {
+// case number: java.lang.Number => nbt.setDouble(key, number.doubleValue())
+// case string: String if !string.isEmpty => nbt.setString(key, string)
+// case _ => // Unsupported, ignore.
+// }
+// }
+ }
+ case _ => // Ignore.
+ }
+ case _ => // Ignore.
+ }
+ }
}
diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala
index aefdd926a..9100c4f7c 100644
--- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala
+++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala
@@ -1,7 +1,9 @@
package li.cil.oc.server.component
+import li.cil.oc.api
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
+import li.cil.oc.api.internal
import li.cil.oc.api.internal.Rotatable
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
@@ -9,7 +11,11 @@ import li.cil.oc.api.machine.Context
import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.common.item.data.NavigationUpgradeData
+import li.cil.oc.util.BlockPosition
+import net.minecraft.entity.player.EntityPlayer
+import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
+import net.minecraft.util.EnumFacing
class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network).
@@ -43,6 +49,21 @@ class UpgradeNavigation(val host: EnvironmentHost with Rotatable) extends prefab
result(size / 2)
}
+ override def onMessage(message: Message): Unit = {
+ super.onMessage(message)
+ if (message.name == "tablet.use") message.source.host match {
+ case machine: api.machine.Machine => (machine.host, message.data) match {
+ case (tablet: internal.Tablet, Array(nbt: NBTTagCompound, stack: ItemStack, player: EntityPlayer, blockPos: BlockPosition, side: EnumFacing, hitX: java.lang.Float, hitY: java.lang.Float, hitZ: java.lang.Float)) =>
+ val info = data.mapData(host.world)
+ nbt.setInteger("posX", blockPos.x - info.xCenter)
+ nbt.setInteger("posY", blockPos.y)
+ nbt.setInteger("posZ", blockPos.z - info.zCenter)
+ case _ => // Ignore.
+ }
+ case _ => // Ignore.
+ }
+ }
+
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) {
diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala
index 2bd5e1c25..ced66d1cc 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala
@@ -7,7 +7,6 @@ import li.cil.oc.api.machine.Context
import li.cil.oc.server.component.result
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
-import net.minecraft.item.ItemStack
trait InventoryAnalytics extends InventoryAware with NetworkAware {
@Callback(doc = """function([slot:number]):table -- Get a description of the stack in the specified slot or the selected slot.""")
@@ -21,12 +20,24 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware {
def storeInternal(context: Context, args: Arguments): Array[AnyRef] = {
val localSlot = args.checkSlot(inventory, 0)
val dbAddress = args.checkString(1)
- def store(stack: ItemStack) = DatabaseAccess.withDatabase(node, dbAddress, database => {
+ val localStack = inventory.getStackInSlot(localSlot)
+ DatabaseAccess.withDatabase(node, dbAddress, database => {
val dbSlot = args.checkSlot(database.data, 2)
val nonEmpty = database.data.getStackInSlot(dbSlot) != null
- database.data.setInventorySlotContents(dbSlot, stack.copy())
+ database.data.setInventorySlotContents(dbSlot, localStack.copy())
result(nonEmpty)
})
- store(inventory.getStackInSlot(localSlot))
+ }
+
+ @Callback(doc = """function(slot:number, dbAddress:string, dbSlot:number):boolean -- Compare an item in the specified slot with one in the database with the specified address.""")
+ def compareToDatabase(context: Context, args: Arguments): Array[AnyRef] = {
+ val localSlot = args.checkSlot(inventory, 0)
+ val dbAddress = args.checkString(1)
+ val localStack = inventory.getStackInSlot(localSlot)
+ DatabaseAccess.withDatabase(node, dbAddress, database => {
+ val dbSlot = args.checkSlot(database.data, 2)
+ val dbStack = database.data.getStackInSlot(dbSlot)
+ result(haveSameItemType(localStack, dbStack))
+ })
}
}
diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala
index 2342fb1d7..5bc635ea2 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala
@@ -23,6 +23,7 @@ trait InventoryAware {
protected def stackInSlot(slot: Int) = Option(inventory.getStackInSlot(slot))
protected def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =
- stackA.getItem == stackB.getItem &&
+ stackA != null && stackB != null &&
+ stackA.getItem == stackB.getItem &&
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage)
}
diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala
index 5891e8e8e..92dfbefe8 100644
--- a/src/main/scala/li/cil/oc/server/machine/Machine.scala
+++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala
@@ -238,6 +238,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
case arg: java.lang.String => arg
case arg: Array[Byte] => arg
case arg: Map[_, _] if arg.isEmpty || arg.head._1.isInstanceOf[String] && arg.head._2.isInstanceOf[String] => arg
+ case arg: NBTTagCompound => arg
case arg =>
OpenComputers.log.warn("Trying to push signal with an unsupported argument of type " + arg.getClass.getName)
null
@@ -247,7 +248,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
}
})
- override def popSignal(): Machine.Signal = signals.synchronized(if (signals.isEmpty) null else signals.dequeue())
+ override def popSignal(): Machine.Signal = signals.synchronized(if (signals.isEmpty) null else signals.dequeue().convert())
override def methods(value: scala.AnyRef) = Callbacks(value).map(entry => {
val (name, callback) = entry
@@ -628,6 +629,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
data += tag.getStringTagAt(i) -> tag.getStringTagAt(i + 1)
}
data
+ case tag: NBTTagCompound => tag
case _ => null
}.toArray[AnyRef])
})
@@ -705,6 +707,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
list.append(value.toString)
}
args.setTag("arg" + i, list)
+ case (arg: NBTTagCompound, i) => args.setTag("arg" + i, arg)
case (_, i) => args.setByte("arg" + i, -1)
}
})
@@ -936,7 +939,9 @@ object Machine extends MachineAPI {
}
/** Signals are messages sent to the Lua state from Java asynchronously. */
- private[machine] class Signal(val name: String, val args: Array[AnyRef]) extends machine.Signal
+ private[machine] class Signal(val name: String, val args: Array[AnyRef]) extends machine.Signal {
+ def convert() = new Signal(name, Registry.convert(args))
+ }
private val threadPool = ThreadPoolFactory.create("Computer", Settings.get.threads)
}
\ No newline at end of file