diff --git a/assets/items.psd b/assets/items.psd index 78087d3e9..944dd993d 100644 Binary files a/assets/items.psd and b/assets/items.psd differ diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index bfcd52137..4f31b80a5 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -746,6 +746,12 @@ opencomputers { # does not consume energy on a per-use basis. When standing still or # moving very slowly this also does not trigger. hoverBootMove: 1 + + # Cost for simple operations on the data card, such as hashing. + dataCardSimple: 0.25 + + # Cost for complex operations on the data card, such as inflate/deflate. + dataCardComplex: 4.0 } # The rate at which different blocks accept external power. All of these @@ -1125,6 +1131,18 @@ opencomputers { # happen one time per game, not per world, not per death. Once. If this # is still too much for your taste, disable it here ;-) giveManualToNewPlayers: true + + # Soft limit for size of byte arrays passed to data card callbacks. If this + # limit is exceeded, a longer sleep is enforced (see dataCardTimeout). + dataCardSoftLimit: 8192 + + # Hard limit for size of byte arrays passed to data card callbacks. If this + # limit is exceeded, the call fails and does nothing. + dataCardHardLimit: 1048576 + + # Time in seconds to pause a calling machine when the soft limit for a data + # card callback is exceeded. + dataCardTimeout: 1.0 } # Settings for mod integration (the mod previously known as OpenComponents). diff --git a/src/main/resources/assets/opencomputers/textures/items/DataCard.png b/src/main/resources/assets/opencomputers/textures/items/DataCard.png new file mode 100644 index 000000000..88cac2868 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/DataCard.png differ diff --git a/src/main/resources/assets/opencomputers/textures/items/DataCard.png.mcmeta b/src/main/resources/assets/opencomputers/textures/items/DataCard.png.mcmeta new file mode 100644 index 000000000..341f7f627 --- /dev/null +++ b/src/main/resources/assets/opencomputers/textures/items/DataCard.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "frametime": 1, + "frames": [ + { "index": 0, "time": 4 }, + { "index": 1, "time": 4 }, + { "index": 2, "time": 4 }, + { "index": 3, "time": 4 }, + { "index": 4, "time": 4 }, + { "index": 5, "time": 4 }, + { "index": 6, "time": 4 }, + { "index": 7, "time": 4 } + ] + } +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index f957f9a55..4920c2aa9 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -193,6 +193,8 @@ class Settings(val config: Config) { val hoverBootJump = config.getDouble("power.cost.hoverBootJump") max 0 val hoverBootAbsorb = config.getDouble("power.cost.hoverBootAbsorb") max 0 val hoverBootMove = config.getDouble("power.cost.hoverBootMove") max 0 + val dataCardSimple = config.getDouble("power.cost.dataCardSimple") max 0 + val dataCardComplex = config.getDouble("power.cost.dataCardComplex") max 0 // power.rate val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 @@ -312,6 +314,9 @@ class Settings(val config: Config) { val assemblerBlacklist = config.getStringList("misc.assemblerBlacklist") val threadPriority = config.getInt("misc.threadPriority") val giveManualToNewPlayers = config.getBoolean("misc.giveManualToNewPlayers") + val dataCardSoftLimit = config.getInt("misc.dataCardSoftLimit") max 0 + val dataCardHardLimit = config.getInt("misc.dataCardHardLimit") max 0 + val dataCardTimeout = config.getDouble("misc.dataCardTimeout") max 0 // ----------------------------------------------------------------------- // // printer diff --git a/src/main/scala/li/cil/oc/common/item/DataCard.scala b/src/main/scala/li/cil/oc/common/item/DataCard.scala index 61127695f..b0a487249 100644 --- a/src/main/scala/li/cil/oc/common/item/DataCard.scala +++ b/src/main/scala/li/cil/oc/common/item/DataCard.scala @@ -1,3 +1,3 @@ package li.cil.oc.common.item -class DataCard(val parent: Delegator) extends traits.Delegate \ No newline at end of file +class DataCard(val parent: Delegator) extends traits.Delegate diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala index c4c954c2c..94b1322ee 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverDataCard.scala @@ -1,17 +1,18 @@ package li.cil.oc.integration.opencomputers +import li.cil.oc.api.driver.EnvironmentAware +import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.common.Slot import li.cil.oc.server.component -import li.cil.oc.{Constants, api} -import li.cil.oc.api.driver.{EnvironmentHost, EnvironmentAware} +import li.cil.oc.Constants +import li.cil.oc.api import net.minecraft.item.ItemStack - -object DriverDataCard extends Item with EnvironmentAware{ +object DriverDataCard extends Item with EnvironmentAware { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get(Constants.ItemName.DataCard)) - override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = new component.DataCard + override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = new component.DataCard() override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/server/component/DataCard.scala b/src/main/scala/li/cil/oc/server/component/DataCard.scala index 83e60a6c6..b6e12d76a 100644 --- a/src/main/scala/li/cil/oc/server/component/DataCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DataCard.scala @@ -1,19 +1,25 @@ package li.cil.oc.server.component -import java.security.MessageDigest -import java.util.zip.{InflaterOutputStream, DeflaterOutputStream} +import java.util.zip.DeflaterOutputStream +import java.util.zip.InflaterOutputStream import com.google.common.hash.Hashing +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.Network +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.Node +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.prefab import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.{Settings, OpenComputers, api} -import li.cil.oc.api.machine.{Arguments, Context, Callback} -import li.cil.oc.api.network.{Node, Visibility} -import li.cil.oc.api.{Network, prefab} import net.minecraft.nbt.NBTTagCompound import org.apache.commons.codec.binary.Base64 import org.apache.commons.io.output.ByteArrayOutputStream -class DataCard extends prefab.ManagedEnvironment{ +class DataCard extends prefab.ManagedEnvironment { override val node = Network.newNode(this, Visibility.Neighbors). withComponent("data", Visibility.Neighbors). withConnector(). @@ -21,48 +27,66 @@ class DataCard extends prefab.ManagedEnvironment{ val romData = Option(api.FileSystem.asManagedEnvironment(api.FileSystem. fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/component/data"), "data")) - - @Callback(direct = true, doc = """function(data:string):string -- Applies base64 encoding to the data.""", limit = 32) + + @Callback(direct = true, doc = """function():number -- The maximum size of data that can be passed to other functions of the card.""") + def getLimit(context: Context, args: Arguments): Array[AnyRef] = { + result(Settings.get.dataCardHardLimit) + } + + @Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 encoding to the data.""") def encode64(context: Context, args: Arguments): Array[AnyRef] = { - result(Base64.encodeBase64(args.checkByteArray(0))) + result(Base64.encodeBase64(checkLimits(context, args, Settings.get.dataCardComplex))) } - @Callback(direct = true, doc = """function(data:string):string -- Applies base64 decoding to the data.""", limit = 32) + @Callback(direct = true, limit = 32, doc = """function(data:string):string -- Applies base64 decoding to the data.""") def decode64(context: Context, args: Arguments): Array[AnyRef] = { - result(Base64.decodeBase64(args.checkByteArray(0))) + result(Base64.decodeBase64(checkLimits(context, args, Settings.get.dataCardComplex))) } - @Callback(direct = true, doc = """function(data:string):string -- Applies deflate compression to the data.""", limit = 6) + @Callback(direct = true, limit = 6, doc = """function(data:string):string -- Applies deflate compression to the data.""") def deflate(context: Context, args: Arguments): Array[AnyRef] = { + val data = checkLimits(context, args, Settings.get.dataCardComplex) val baos = new ByteArrayOutputStream(512) val deos = new DeflaterOutputStream(baos) - deos.write(args.checkByteArray(0)) + deos.write(data) deos.finish() result(baos.toByteArray) } - @Callback(direct = true, doc = """function(data:string):string -- Applies inflate decompression to the data.""", limit = 6) + @Callback(direct = true, limit = 6, doc = """function(data:string):string -- Applies inflate decompression to the data.""") def inflate(context: Context, args: Arguments): Array[AnyRef] = { + val data = checkLimits(context, args, Settings.get.dataCardComplex) val baos = new ByteArrayOutputStream(512) val inos = new InflaterOutputStream(baos) - inos.write(args.checkByteArray(0)) + inos.write(data) inos.finish() result(baos.toByteArray) } - @Callback(direct = true, doc = """function(data:string):string -- Computes SHA2-256 hash of the data. Result is in binary format.""", limit = 32) + @Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes SHA2-256 hash of the data. Result is in binary format.""") def sha256(context: Context, args: Arguments): Array[AnyRef] = { - result(Hashing.sha256().hashBytes(args.checkByteArray(0)).asBytes()) + val data = checkLimits(context, args, Settings.get.dataCardSimple) + result(Hashing.sha256().hashBytes(data).asBytes()) } - @Callback(direct = true, doc = """function(data:string):string -- Computes MD5 hash of the data. Result is in binary format""", limit = 32) + @Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes MD5 hash of the data. Result is in binary format""") def md5(context: Context, args: Arguments): Array[AnyRef] = { - result(Hashing.md5().hashBytes(args.checkByteArray(0)).asBytes()) + val data = checkLimits(context, args, Settings.get.dataCardSimple) + result(Hashing.md5().hashBytes(data).asBytes()) } - @Callback(direct = true, doc = """function(data:string):string -- Computes CRC-32 hash of the data. Result is in binary format""", limit = 32) + @Callback(direct = true, limit = 32, doc = """function(data:string):string -- Computes CRC-32 hash of the data. Result is in binary format""") def crc32(context: Context, args: Arguments): Array[AnyRef] = { - result(Hashing.crc32().hashBytes(args.checkByteArray(0)).asBytes()) + val data = checkLimits(context, args, Settings.get.dataCardSimple) + result(Hashing.crc32().hashBytes(data).asBytes()) + } + + private def checkLimits(context: Context, args: Arguments, cost: Double): Array[Byte] = { + val data = args.checkByteArray(0) + if (data.length > Settings.get.dataCardHardLimit) throw new IllegalArgumentException("data size limit exceeded") + if (!node.tryChangeBuffer(-cost)) throw new Exception("not enough energy") + if (data.length > Settings.get.dataCardSoftLimit) context.pause(Settings.get.dataCardTimeout) + data } // ----------------------------------------------------------------------- //