diff --git a/li/cil/oc/api/network/Arguments.java b/li/cil/oc/api/network/Arguments.java
index ad15fb21f..1db862dbb 100644
--- a/li/cil/oc/api/network/Arguments.java
+++ b/li/cil/oc/api/network/Arguments.java
@@ -5,6 +5,10 @@ package li.cil.oc.api.network;
*
* It allows checking for the presence of arguments in a uniform manner, taking
* care of proper type checking based on what can be passed along by Lua.
+ *
+ * Note that integer values fetched this way are actually double values that
+ * have been truncated. So if a Lua program passes 1.9 and you do a
+ * checkInteger you'll get a 1 .
*/
public interface Arguments extends Iterable {
/**
@@ -28,16 +32,125 @@ public interface Arguments extends Iterable {
*
* @param index the index from which to get the argument.
* @return the raw value at that index.
+ * @throws IllegalArgumentException if there is no argument at that index.
*/
Object checkAny(int index);
+ /**
+ * Try to get a boolean value at the specified index.
+ *
+ * Throws an error if there are too few arguments.
+ *
+ * @param index the index from which to get the argument.
+ * @return the boolean value at the specified index.
+ * @throws IllegalArgumentException if there is no argument at that index,
+ * or if the argument is not a boolean.
+ */
boolean checkBoolean(int index);
- double checkDouble(int index);
-
+ /**
+ * Try to get an integer value at the specified index.
+ *
+ * Throws an error if there are too few arguments.
+ *
+ * @param index the index from which to get the argument.
+ * @return the integer value at the specified index.
+ * @throws IllegalArgumentException if there is no argument at that index,
+ * or if the argument is not a number.
+ */
int checkInteger(int index);
+ /**
+ * Try to get a double value at the specified index.
+ *
+ * Throws an error if there are too few arguments.
+ *
+ * @param index the index from which to get the argument.
+ * @return the double value at the specified index.
+ * @throws IllegalArgumentException if there is no argument at that index,
+ * or if the argument is not a number.
+ */
+ double checkDouble(int index);
+
+ /**
+ * Try to get a string value at the specified index.
+ *
+ * Throws an error if there are too few arguments.
+ *
+ * This will actually check for a byte array and convert it to a string
+ * using UTF-8 encoding.
+ *
+ * @param index the index from which to get the argument.
+ * @return the boolean value at the specified index.
+ * @throws IllegalArgumentException if there is no argument at that index,
+ * or if the argument is not a string.
+ */
String checkString(int index);
+ /**
+ * Try to get a byte array at the specified index.
+ *
+ * Throws an error if there are too few arguments.
+ *
+ * @param index the index from which to get the argument.
+ * @return the byte array at the specified index.
+ * @throws IllegalArgumentException if there is no argument at that index,
+ * or if the argument is not a byte array.
+ */
byte[] checkByteArray(int index);
+
+ /**
+ * Tests whether the argument at the specified index is a boolean value.
+ *
+ * This will return true if there is no argument at the specified
+ * index, i.e. if there are too few arguments.
+ *
+ * @param index the index to check.
+ * @return true if the argument is a boolean; false otherwise.
+ */
+ boolean isBoolean(int index);
+
+ /**
+ * Tests whether the argument at the specified index is an integer value.
+ *
+ * This will return true if there is no argument at the specified
+ * index, i.e. if there are too few arguments.
+ *
+ * @param index the index to check.
+ * @return true if the argument is an integer; false otherwise.
+ */
+ boolean isInteger(int index);
+
+ /**
+ * Tests whether the argument at the specified index is a double value.
+ *
+ * This will return true if there is no argument at the specified
+ * index, i.e. if there are too few arguments.
+ *
+ * @param index the index to check.
+ * @return true if the argument is a double; false otherwise.
+ */
+ boolean isDouble(int index);
+
+ /**
+ * Tests whether the argument at the specified index is a string value.
+ *
+ * This will return true if there is no argument at the specified
+ * index, i.e. if there are too few arguments.
+ *
+ * @param index the index to check.
+ * @return true if the argument is a string; false otherwise.
+ */
+ boolean isString(int index);
+
+ /**
+ * Tests whether the argument at the specified index is a byte array.
+ *
+ * This will return true if there is no argument at the specified
+ * index, i.e. if there are too few arguments.
+ *
+ * @param index the index to check.
+ * @return true if the argument is a byte array; false otherwise.
+ */
+ boolean isByteArray(int index);
}
diff --git a/li/cil/oc/common/component/Screen.scala b/li/cil/oc/common/component/Screen.scala
index ee0ecd171..bc84864ce 100644
--- a/li/cil/oc/common/component/Screen.scala
+++ b/li/cil/oc/common/component/Screen.scala
@@ -74,12 +74,12 @@ object Screen {
trait Environment extends tileentity.Environment with util.Persistable {
val node = api.Network.newNode(this, Visibility.Network).
withComponent("screen").
- withConnector(Config.bufferScreen).
+ withConnector(Config.bufferScreen * (tier + 1)).
create()
- final val instance = new component.Screen(this, maxResolution)
+ final val instance = new component.Screen(this, Config.screenResolutionsByTier(tier))
- protected def maxResolution: (Int, Int)
+ protected def tier: Int
// ----------------------------------------------------------------------- //
diff --git a/li/cil/oc/common/tileentity/Screen.scala b/li/cil/oc/common/tileentity/Screen.scala
index def9b5d36..59ed6d4b2 100644
--- a/li/cil/oc/common/tileentity/Screen.scala
+++ b/li/cil/oc/common/tileentity/Screen.scala
@@ -14,15 +14,15 @@ import scala.collection.mutable
import net.minecraft.client.Minecraft
class ScreenTier1 extends Screen {
- protected def maxResolution = Config.screenResolutionsByTier(0)
+ protected def tier = 0
}
class ScreenTier2 extends Screen {
- protected def maxResolution = Config.screenResolutionsByTier(1)
+ protected def tier = 1
}
class ScreenTier3 extends Screen {
- protected def maxResolution = Config.screenResolutionsByTier(2)
+ protected def tier = 2
}
abstract class Screen extends Rotatable with ScreenEnvironment {
@@ -165,7 +165,7 @@ abstract class Screen extends Rotatable with ScreenEnvironment {
def tryMergeTowards(dx: Int, dy: Int) = {
val (nx, ny, nz) = unproject(x + dx, y + dy, z)
worldObj.getBlockTileEntity(nx, ny, nz) match {
- case s: Screen if s.maxResolution == maxResolution && s.pitch == pitch && s.yaw == yaw && !screens.contains(s) =>
+ case s: Screen if s.tier == tier && s.pitch == pitch && s.yaw == yaw && !screens.contains(s) =>
val (sx, sy, _) = project(s.origin)
val canMergeAlongX = sy == y && s.height == height && s.width + width <= Config.maxScreenWidth
val canMergeAlongY = sx == x && s.width == width && s.height + height <= Config.maxScreenHeight
diff --git a/li/cil/oc/server/component/Carriage.scala b/li/cil/oc/server/component/Carriage.scala
index 52fff5377..bafb632c3 100644
--- a/li/cil/oc/server/component/Carriage.scala
+++ b/li/cil/oc/server/component/Carriage.scala
@@ -1,49 +1,71 @@
package li.cil.oc.server.component
-import java.lang.reflect.InvocationTargetException
import li.cil.oc.api
import li.cil.oc.api.network._
+import li.cil.oc.util.RedstoneInMotion
import net.minecraft.nbt.NBTTagCompound
-import scala.Some
-import scala.language.existentials
-class Carriage(controller: Object) extends ManagedComponent {
+class Carriage(controller: AnyRef) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Network).
withComponent("carriage").
create()
- private val (directions, setup, move) = try {
- val directions = Class.forName("JAKJ.RedstoneInMotion.Directions").getEnumConstants
- val clazz = Class.forName("JAKJ.RedstoneInMotion.CarriageControllerEntity")
- val methods = clazz.getDeclaredMethods
- val setup = methods.find(_.getName == "SetupMotion").orNull
- val move = methods.find(_.getName == "Move").orNull
- (directions, setup, move)
- } catch {
- case _: Throwable => (null, null, null)
- }
+ private val names = Map(
+ "negy" -> 0, "posy" -> 1, "negz" -> 2, "posz" -> 3, "negx" -> 4, "posx" -> 5,
+ "down" -> 0, "up" -> 1, "north" -> 2, "south" -> 3, "west" -> 4, "east" -> 5)
- private var shouldMove = false
+ private var anchored = false
private var direction = 0
private var simulating = false
- private var anchored = false
+
+ private var shouldMove = false
private var moving = false
// ----------------------------------------------------------------------- //
@LuaCallback("move")
def move(context: Context, args: Arguments): Array[Object] = {
- if (directions == null || setup == null || move == null)
+ direction = checkDirection(args)
+ simulating = if (args.count > 1) args.checkBoolean(1) else false
+ shouldMove = true
+ result(true)
+ }
+
+ @LuaCallback("simulate")
+ def simulate(context: Context, args: Arguments): Array[Object] = {
+ direction = checkDirection(args)
+ simulating = true
+ shouldMove = true
+ result(true)
+ }
+
+ @LuaCallback(value = "getAnchored", direct = true)
+ def getAnchored(context: Context, args: Arguments): Array[Object] =
+ result(anchored)
+
+ @LuaCallback("setAnchored")
+ def setAnchored(context: Context, args: Arguments): Array[Object] = {
+ anchored = args.checkBoolean(0)
+ result(anchored)
+ }
+
+ private def checkDirection(args: Arguments) = {
+ if (!RedstoneInMotion.available)
throw new Exception("Redstone in Motion not found")
if (shouldMove || moving)
throw new Exception("already moving")
- direction = args.checkInteger(0)
- if (direction < 0 || direction > directions.length)
- throw new ArrayIndexOutOfBoundsException("invalid direction")
- simulating = args.checkBoolean(1)
- anchored = args.checkBoolean(2)
- shouldMove = true
- result(true)
+ if (args.isString(0)) {
+ val name = args.checkString(0).toLowerCase
+ if (!names.contains(name))
+ throw new IllegalArgumentException("invalid direction")
+ names(name)
+ }
+ else {
+ val index = args.checkInteger(0)
+ if (index < 0 || index > 5)
+ throw new ArrayIndexOutOfBoundsException("invalid direction")
+ index
+ }
}
// ----------------------------------------------------------------------- //
@@ -53,18 +75,19 @@ class Carriage(controller: Object) extends ManagedComponent {
if (shouldMove) {
shouldMove = false
moving = true
- var error: Option[Throwable] = None
try {
- setup.invoke(controller, directions(direction), Boolean.box(simulating), Boolean.box(anchored))
- move.invoke(controller)
- } catch {
- case e: InvocationTargetException => error = Some(e.getCause)
- case e: Throwable => error = Some(e)
+ RedstoneInMotion.move(controller, direction, simulating, anchored)
+ if (simulating || anchored) {
+ // We won't get re-connected, so we won't send in onConnect. Do it here.
+ node.sendToReachable("computer.signal", "carriage_moved", Boolean.box(true))
+ }
}
- moving = false
- error match {
- case Some(e) => node.sendToReachable("computer.signal", "carriage_moved", Unit, Option(e.getMessage).getOrElse(e.toString))
- case _ => if (simulating || anchored) node.sendToReachable("computer.signal", "carriage_moved", Boolean.box(true))
+ catch {
+ case e: Throwable =>
+ node.sendToReachable("computer.signal", "carriage_moved", Unit, Option(e.getMessage).getOrElse(e.toString))
+ }
+ finally {
+ moving = false
}
}
}
@@ -84,10 +107,12 @@ class Carriage(controller: Object) extends ManagedComponent {
override def save(nbt: NBTTagCompound) {
super.save(nbt)
nbt.setBoolean("moving", moving)
+ nbt.setBoolean("anchored", anchored)
}
override def load(nbt: NBTTagCompound) {
super.load(nbt)
moving = nbt.getBoolean("moving")
+ anchored = nbt.getBoolean("anchored")
}
}
diff --git a/li/cil/oc/server/driver/Carriage.scala b/li/cil/oc/server/driver/Carriage.scala
index b77acaaf9..6d405e973 100644
--- a/li/cil/oc/server/driver/Carriage.scala
+++ b/li/cil/oc/server/driver/Carriage.scala
@@ -2,28 +2,19 @@ package li.cil.oc.server.driver
import li.cil.oc.api.driver
import li.cil.oc.server.component
+import li.cil.oc.util.RedstoneInMotion
import net.minecraft.world.World
object Carriage extends driver.Block {
- private val (carriageControllerClass) = try {
- Class.forName("JAKJ.RedstoneInMotion.CarriageControllerEntity")
- } catch {
- case _: Throwable => null
- }
-
def worksWith(world: World, x: Int, y: Int, z: Int) =
Option(world.getBlockTileEntity(x, y, z)) match {
- case Some(entity) if checkClass(entity) => true
+ case Some(entity) if RedstoneInMotion.isCarriageController(entity) => true
case _ => false
}
def createEnvironment(world: World, x: Int, y: Int, z: Int) =
world.getBlockTileEntity(x, y, z) match {
- case controller if checkClass(controller) =>
- new component.Carriage(controller)
+ case entity if RedstoneInMotion.isCarriageController(entity) => new component.Carriage(entity)
case _ => null
}
-
- private def checkClass(value: Object) =
- carriageControllerClass != null && carriageControllerClass.isAssignableFrom(value.getClass)
}
diff --git a/li/cil/oc/server/network/Component.scala b/li/cil/oc/server/network/Component.scala
index db63e7175..4c116392f 100644
--- a/li/cil/oc/server/network/Component.scala
+++ b/li/cil/oc/server/network/Component.scala
@@ -146,7 +146,7 @@ object Component {
args(index)
}
- def checkBoolean(index: Int): Boolean = {
+ def checkBoolean(index: Int) = {
checkIndex(index, "boolean")
args(index) match {
case value: java.lang.Boolean => value
@@ -154,7 +154,7 @@ object Component {
}
}
- def checkDouble(index: Int): Double = {
+ def checkDouble(index: Int) = {
checkIndex(index, "number")
args(index) match {
case value: java.lang.Double => value
@@ -162,7 +162,7 @@ object Component {
}
}
- def checkInteger(index: Int): Int = {
+ def checkInteger(index: Int) = {
checkIndex(index, "number")
args(index) match {
case value: java.lang.Double => value.intValue
@@ -170,10 +170,16 @@ object Component {
}
}
- def checkString(index: Int) =
- new String(checkByteArray(index), "UTF-8")
+ def checkString(index: Int) = {
+ checkIndex(index, "string")
+ args(index) match {
+ case value: java.lang.String => value
+ case value: Array[Byte] => new String(value, "UTF-8")
+ case value => throw typeError(index, value, "string")
+ }
+ }
- def checkByteArray(index: Int): Array[Byte] = {
+ def checkByteArray(index: Int) = {
checkIndex(index, "string")
args(index) match {
case value: Array[Byte] => value
@@ -181,6 +187,37 @@ object Component {
}
}
+ def isBoolean(index: Int) =
+ index >= 0 && index < count && (args(index) match {
+ case value: java.lang.Boolean => true
+ case _ => false
+ })
+
+ def isDouble(index: Int) =
+ index >= 0 && index < count && (args(index) match {
+ case value: java.lang.Double => true
+ case _ => false
+ })
+
+ def isInteger(index: Int) =
+ index >= 0 && index < count && (args(index) match {
+ case value: java.lang.Integer => true
+ case _ => false
+ })
+
+ def isString(index: Int) =
+ index >= 0 && index < count && (args(index) match {
+ case value: java.lang.String => true
+ case value: Array[Byte] => true
+ case _ => false
+ })
+
+ def isByteArray(index: Int) =
+ index >= 0 && index < count && (args(index) match {
+ case value: Array[Byte] => true
+ case _ => false
+ })
+
private def checkIndex(index: Int, name: String) =
if (index < 0) throw new IndexOutOfBoundsException()
else if (args.length <= index) throw new IllegalArgumentException(
diff --git a/li/cil/oc/util/RedstoneInMotion.scala b/li/cil/oc/util/RedstoneInMotion.scala
new file mode 100644
index 000000000..a12098211
--- /dev/null
+++ b/li/cil/oc/util/RedstoneInMotion.scala
@@ -0,0 +1,36 @@
+package li.cil.oc.util
+
+import java.lang.reflect.InvocationTargetException
+import scala.language.existentials
+
+object RedstoneInMotion {
+ private val (controller, setup, move, directions) = try {
+ val controller = Class.forName("JAKJ.RedstoneInMotion.CarriageControllerEntity")
+ val methods = controller.getDeclaredMethods
+ val setup = methods.find(_.getName == "SetupMotion").get
+ val move = methods.find(_.getName == "Move").get
+ val directions = Class.forName("JAKJ.RedstoneInMotion.Directions").getEnumConstants
+ (Option(controller), setup, move, directions)
+ } catch {
+ case _: Throwable => (None, null, null, null)
+ }
+
+ def available = controller.isDefined
+
+ def isCarriageController(value: AnyRef) = controller match {
+ case Some(clazz) => clazz.isAssignableFrom(value.getClass)
+ case _ => false
+ }
+
+ def move(controller: AnyRef, direction: Int, simulating: Boolean, anchored: Boolean) {
+ if (!isCarriageController(controller))
+ throw new IllegalArgumentException("Not a carriage controller.")
+
+ try {
+ setup.invoke(controller, directions(direction), Boolean.box(simulating), Boolean.box(anchored))
+ move.invoke(controller)
+ } catch {
+ case e: InvocationTargetException => throw e.getCause
+ }
+ }
+}