mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-15 10:21:45 -04:00
cleaned up RIM reflection into a helper singleton; cleaned up and extended RIM component a bit; extended arguments class with type testing methods; documented arguments class; linearly increasing screen buffer size per tier (copy would fail for advanced screens...)
This commit is contained in:
parent
43d9365c17
commit
2a2785e077
@ -5,6 +5,10 @@ package li.cil.oc.api.network;
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* Note that integer values fetched this way are actually double values that
|
||||
* have been truncated. So if a Lua program passes <tt>1.9</tt> and you do a
|
||||
* <tt>checkInteger</tt> you'll get a <tt>1</tt>.
|
||||
*/
|
||||
public interface Arguments extends Iterable<Object> {
|
||||
/**
|
||||
@ -28,16 +32,125 @@ public interface Arguments extends Iterable<Object> {
|
||||
*
|
||||
* @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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* Throws an error if there are too few arguments.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* This will return true if there is <em>no</em> 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.
|
||||
* <p/>
|
||||
* This will return true if there is <em>no</em> 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.
|
||||
* <p/>
|
||||
* This will return true if there is <em>no</em> 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.
|
||||
* <p/>
|
||||
* This will return true if there is <em>no</em> 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.
|
||||
* <p/>
|
||||
* This will return true if there is <em>no</em> 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(
|
||||
|
36
li/cil/oc/util/RedstoneInMotion.scala
Normal file
36
li/cil/oc/util/RedstoneInMotion.scala
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user