Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into master-MC1.8

Conflicts:
	src/main/scala/li/cil/oc/common/item/Acid.scala
	src/main/scala/li/cil/oc/common/item/Nanomachines.scala
This commit is contained in:
Florian Nücke 2015-09-19 00:55:14 +02:00
commit a76bdb4552
10 changed files with 120 additions and 64 deletions

View File

@ -12,7 +12,7 @@ import li.cil.oc.api.detail.*;
*/
public class API {
public static final String ID_OWNER = "OpenComputers|Core";
public static final String VERSION = "5.6.0";
public static final String VERSION = "5.6.1";
public static DriverAPI driver = null;
public static FileSystemAPI fileSystem = null;

View File

@ -44,15 +44,24 @@ public interface Controller {
int getTotalInputCount();
/**
* Get the total number of inputs that may be active at the same time
* Get the number of inputs that may be active at the same time
* before negative effects are applied to the player.
* <p/>
* The number of active inputs may exceed this value, but this will
* have negative effects on the player.
*
* @return the number of inputs that may safely be active at a time.
*/
int getSafeActiveInputs();
/**
* Get the total number of inputs that may be active at the same time.
* <p/>
* The number of active inputs cannot exceed this value.
*
* @return the number of inputs that may be active at a time.
*/
int getSafeInputCount();
int getMaxActiveInputs();
/**
* Get whether the input with the specified index is active.
@ -65,12 +74,16 @@ public interface Controller {
/**
* Set the state of the input with the specified index.
* <p/>
* This will fail if too many inputs are active already. It will also
* always fail when called on the client.
*
* @param index the input index.
* @param value whether the input should be active.
* @return whether the input was changed successfully.
* @throws IndexOutOfBoundsException if <code>index &lt; 0</code> or <code>index &gt;= getInputCount</code>.
*/
void setInput(int index, boolean value);
boolean setInput(int index, boolean value);
/**
* Get the list of currently active behaviors, based on the current input states.

View File

@ -1011,11 +1011,21 @@ opencomputers {
# How many input nodes may be active at the same time before negative
# effects are applied to the player.
safeInputCount: 2
safeInputsActive: 2
# The time in seconds it takes to reconfigure the nanomachines. This is
# to avoid spamming reconfigurations.
reconfigureCooldown: 5
# Hard maximum number of active inputs. This is mainly to avoid people
# bumping other nanomachines' inputs to max, killing them in a matter
# of (milli)seconds.
maxInputsActive: 4
# Time in seconds it takes for the nanomachines to process a command
# and send a response.
commandDelay: 1
# The distance in blocks that nanomachines can communicate within. If
# a message comes from further away, it'll be ignored. When responding,
# the response will only be sent this far.
commandRange: 2
# Range of the item magnet behavior added for each active input.
magnetRange: 8

View File

@ -4,4 +4,6 @@
This tasty [citation needed] concoction can be consumed if you ever feel the need for some... fun. Or ruining your digestive tract. Or both. It can also serve as ingredient in other, more useful items.
One of the main uses, however, is to remove [nanomachines](nanomachines.md) from your system, if you no longer want them in you. Drinking this is the only way of getting rid of them!
When using hard-mode recipes, it is used to etch [circuit boards](circuitBoard.md) before crafting [printed circuit boards](printedCircuitBoard.md).

View File

@ -8,22 +8,26 @@ Once injected, a new power indicator in your HUD will indicate how much energy y
Nanomachines provide a certain number of "inputs" that can be triggered, causing many different effects on the player, ranging from visual effects such as particles spawning near the player, to select potion effects and some more rare and special behaviors!
Which input triggers what effect depends on the current configuration of the nanomachines, the actual "connections" being random per configuration. This means you'll have to try enabling different inputs to see what they do. If you're unhappy with a configuration, you can always reconfigure your nanomachines. Beware that enabling too many inputs at a time has severe negative effects on you!
Which input triggers what effect depends on the current configuration of the nanomachines, the actual "connections" being random per configuration. This means you'll have to try enabling different inputs to see what they do. If you're unhappy with a configuration, you can always reconfigure your nanomachines by injecting a new batch (just eat some more). To completely get rid of the nanomachines in you, consider drinking some [grog](acid.md). Beware that enabling too many inputs at a time has severe negative effects on you!
By default, the nanomachines will be on standby. You'll need to control them using wireless messages, so carrying a [tablet](tablet.md) with a [wireless network card](wlanCard.md) is strongly recommended. Nanomachines will only react to wireless signals emitted by devices no further than two meters away, but they will react to messages on any port, and from any device!
Nanomachines react to a simple, proprietary protocol: each packet must consist of multiple parts, the first of which is the "header" and must equal the string `nanomachines`. The second part must be the command name. Additional parts are parameters for the command. The following commands are available, formatted as `commandName(arg1, ...)`:
- `setResponsePort(port:number)` - Set the port nanomachines should send response messages to, for commands that have a response.
- `dispose()` - Destroy all nanomachines currently in the player.
- `reconfigure()` - Cause the nanomachines to enter a new configuration.
- `getTotalInputCount()` - Request a message with the total number of available inputs.
- `getSafeInputCount()` - Request a message with the number of *safe* inputs.
- `getInput(index:number)` - Request a message with the current state of the input with the specified index.
- `getPowerState()` - Request the currently stored and maximum stored energy of the nanomachines.
- `getHealth()` - Request the player's health state.
- `getHunger()` - Request the player's hunger state.
- `getAge()` - Request the player's age in seconds.
- `getName()` - Request the player's display name.
- `getExperience()` - Request the player's experience level.
- `getTotalInputCount()` - Request the total number of available inputs.
- `getSafeActiveInputs()` - Request the number of *safe* active inputs.
- `getMaxActiveInputs()` - Request the number of *maximum* active inputs.
- `getInput(index:number)` - Request the current state of the input with the specified index.
- `setInput(index:number, value:boolean)` - Set the state of the input with the specified index to the specified value.
- `getActiveEffects()` - Request a list of active effects. Note that some effects may not show up in this list.
- `getPowerState()` - Request a message with the currently stored and maximum stored energy of the nanomachines.
For example, in OpenOS:
- `component.modem.broadcast(1, "nanomachines", "setInput", 1, true)` will enable the first input.
- `component.modem.broadcast(1, "nanomachines", "reconfigure")` will reconfigure the nanomachines.
- `component.modem.broadcast(1, "nanomachines", "getHealth")` will get the player's health info.

View File

@ -356,8 +356,10 @@ class Settings(val config: Config) {
val nanomachineConnectorQuota = config.getDouble("nanomachines.connectorQuota") max 0
val nanomachineMaxInputs = config.getInt("nanomachines.maxInputs") max 1
val nanomachineMaxOutputs = config.getInt("nanomachines.maxOutputs") max 1
val nanomachinesSafeInputCount = config.getInt("nanomachines.safeInputCount") max 0
val nanomachineReconfigureTimeout = config.getDouble("nanomachines.reconfigureCooldown") max 0
val nanomachinesSafeInputsActive = config.getInt("nanomachines.safeInputsActive") max 0
val nanomachinesMaxInputsActive = config.getInt("nanomachines.maxInputsActive") max 0
val nanomachinesCommandDelay = config.getDouble("nanomachines.commandDelay") max 0
val nanomachinesCommandRange = config.getDouble("nanomachines.commandRange") max 0
val nanomachineMagnetRange = config.getDouble("nanomachines.magnetRange") max 0
val nanomachineDisintegrationRange = config.getInt("nanomachines.disintegrationRange") max 0
val nanomachinePotionWhitelist = config.getAnyRefList("nanomachines.potionWhitelist")

View File

@ -1,5 +1,6 @@
package li.cil.oc.common.item
import li.cil.oc.api
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.EnumAction
import net.minecraft.item.ItemStack
@ -23,11 +24,12 @@ class Acid(val parent: Delegator) extends traits.Delegate {
player.addPotionEffect(new PotionEffect(Potion.poison.id, 100))
player.addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 600))
player.addPotionEffect(new PotionEffect(Potion.confusion.id, 1200))
player.addPotionEffect(new PotionEffect(Potion.fireResistance.id, 6000))
player.addPotionEffect(new PotionEffect(Potion.saturation.id, 2000))
stack.stackSize -= 1
// Remove nanomachines if installed.
api.Nanomachines.uninstallController(player)
}
stack.stackSize -= 1
if (stack.stackSize > 0) stack
else null
}

View File

@ -11,9 +11,7 @@ class Nanomachines(val parent: Delegator) extends traits.Delegate {
override def rarity(stack: ItemStack): EnumRarity = EnumRarity.UNCOMMON
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
if (!api.Nanomachines.hasController(player)) {
player.setItemInUse(stack, getMaxItemUseDuration(stack))
}
player.setItemInUse(stack, getMaxItemUseDuration(stack))
stack
}
@ -22,11 +20,11 @@ class Nanomachines(val parent: Delegator) extends traits.Delegate {
override def getMaxItemUseDuration(stack: ItemStack): Int = 32
override def onItemUseFinish(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
if (!world.isRemote && !api.Nanomachines.hasController(player)) {
if (!world.isRemote) {
// Reconfigure if already installed.
api.Nanomachines.installController(player).reconfigure()
stack.stackSize -= 1
}
stack.stackSize -= 1
if (stack.stackSize > 0) stack
else null
}

View File

@ -32,7 +32,7 @@ import scala.collection.mutable
class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessEndpoint {
if (isServer) api.Network.joinWirelessNetwork(this)
final val MaxSenderDistance = 2f
lazy val CommandRange = Settings.get.nanomachinesCommandRange * Settings.get.nanomachinesCommandRange
final val FullSyncInterval = 20 * 60
final val OverloadDamage = new DamageSourceWithRandomCause("oc.nanomachinesOverload", 3).
@ -41,12 +41,13 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
var uuid = UUID.randomUUID.toString
var responsePort = 0
var commandDelay = 0
var queuedCommand: Option[() => Unit] = None
var storedEnergy = Settings.get.bufferNanomachines * 0.25
var hadPower = true
val configuration = new NeuralNetwork(this)
val activeBehaviors = mutable.Set.empty[Behavior]
var activeBehaviorsDirty = true
var configCooldown = 0
var hasSentConfiguration = false
override def world: World = player.getEntityWorld
@ -58,10 +59,10 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
override def z: Int = BlockPosition(player).z
override def receivePacket(packet: Packet, sender: WirelessEndpoint): Unit = {
if (getLocalBuffer > 0 && !player.isDead) {
if (getLocalBuffer > 0 && commandDelay < 1 && !player.isDead) {
val (dx, dy, dz) = ((sender.x + 0.5) - player.posX, (sender.y + 0.5) - player.posY, (sender.z + 0.5) - player.posZ)
val dSquared = dx * dx + dy * dy + dz * dz
if (dSquared < MaxSenderDistance * MaxSenderDistance) packet.data.headOption match {
if (dSquared <= CommandRange) packet.data.headOption match {
case Some(header: Array[Byte]) if new String(header, Charsets.UTF_8) == "nanomachines" =>
val command = packet.data.drop(1).map {
case value: Array[Byte] => new String(value, Charsets.UTF_8)
@ -70,17 +71,27 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
command match {
case Array("setResponsePort", port: java.lang.Number) =>
responsePort = port.intValue max 0 min 0xFFFF
respond(sender, "responsePort", responsePort)
case Array("dispose") =>
api.Nanomachines.uninstallController(player)
respond(sender, "disposed")
case Array("reconfigure") =>
reconfigure()
respond(sender, "reconfigured")
respond(sender, "port", responsePort)
case Array("getPowerState") =>
respond(sender, "power", getLocalBuffer, getLocalBufferSize)
case Array("getHealth") =>
respond(sender, "health", player.getHealth, player.getMaxHealth)
case Array("getHunger") =>
respond(sender, "hunger", player.getFoodStats.getFoodLevel, player.getFoodStats.getSaturationLevel)
case Array("getAge") =>
respond(sender, "age", (player.getAge / 20f).toInt)
case Array("getName") =>
respond(sender, "name", player.getDisplayName)
case Array("getExperience") =>
respond(sender, "experience", player.experienceLevel)
case Array("getTotalInputCount") =>
respond(sender, "totalInputCount", getTotalInputCount)
case Array("getSafeInputCount") =>
respond(sender, "safeInputCount", getSafeInputCount)
case Array("getSafeActiveInputs") =>
respond(sender, "safeActiveInputs", getSafeActiveInputs)
case Array("getMaxActiveInputs") =>
respond(sender, "maxActiveInputs", getMaxActiveInputs)
case Array("getInput", index: java.lang.Number) =>
try {
val trigger = getInput(index.intValue - 1)
@ -92,8 +103,12 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
}
case Array("setInput", index: java.lang.Number, value: java.lang.Boolean) =>
try {
setInput(index.intValue - 1, value.booleanValue)
respond(sender, "input", index.intValue, getInput(index.intValue - 1))
if (setInput(index.intValue - 1, value.booleanValue)) {
respond(sender, "input", index.intValue, getInput(index.intValue - 1))
}
else {
respond(sender, "input", "too many active inputs")
}
}
catch {
case _: Throwable =>
@ -103,10 +118,8 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
configuration.synchronized {
val names = getActiveBehaviors.map(_.getNameHint).filterNot(Strings.isNullOrEmpty)
val joined = "{" + names.map(_.replace(',', '_').replace('"', '_')).mkString(",") + "}"
respond(sender, "active", joined)
respond(sender, "effects", joined)
}
case Array("getPowerState") =>
respond(sender, "power", getLocalBuffer, getLocalBufferSize)
case _ => // Ignore.
}
case _ => // Not for us.
@ -115,23 +128,25 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
}
def respond(endpoint: WirelessEndpoint, data: Any*): Unit = {
if (responsePort > 0) {
val cost = Settings.get.wirelessCostPerRange * 10
val epsilon = 0.1
if (changeBuffer(-cost) > -epsilon) {
val packet = api.Network.newPacket(uuid, null, responsePort, (Iterable("nanomachines") ++ data.map(_.asInstanceOf[AnyRef])).toArray)
api.Network.sendWirelessPacket(this, 10, packet)
queuedCommand = Option(() => {
if (responsePort > 0) {
val cost = Settings.get.wirelessCostPerRange * CommandRange
val epsilon = 0.1
if (changeBuffer(-cost) > -epsilon) {
val packet = api.Network.newPacket(uuid, null, responsePort, (Iterable("nanomachines") ++ data.map(_.asInstanceOf[AnyRef])).toArray)
api.Network.sendWirelessPacket(this, CommandRange, packet)
}
}
}
})
commandDelay = (Settings.get.nanomachinesCommandDelay * 20).toInt
}
// ----------------------------------------------------------------------- //
override def reconfigure() = {
if (isServer && configCooldown < 1) configuration.synchronized {
if (isServer) configuration.synchronized {
configuration.reconfigure()
activeBehaviorsDirty = true
configCooldown = (Settings.get.nanomachineReconfigureTimeout * 20).toInt
player match {
case playerMP: EntityPlayerMP if playerMP.playerNetServerHandler != null =>
@ -149,14 +164,19 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
override def getTotalInputCount: Int = configuration.synchronized(configuration.triggers.length)
override def getSafeInputCount: Int = Settings.get.nanomachinesSafeInputCount
override def getSafeActiveInputs: Int = Settings.get.nanomachinesSafeInputsActive
override def getMaxActiveInputs: Int = Settings.get.nanomachinesMaxInputsActive
override def getInput(index: Int): Boolean = configuration.synchronized(configuration.triggers(index).isActive)
override def setInput(index: Int, value: Boolean): Unit = {
if (isServer && configCooldown < 1) configuration.synchronized {
configuration.triggers(index).isActive = value
activeBehaviorsDirty = true
override def setInput(index: Int, value: Boolean): Boolean = {
isServer && configuration.synchronized {
(!value || configuration.triggers.count(_.isActive) < Settings.get.nanomachinesMaxInputsActive) && {
configuration.triggers(index).isActive = value
activeBehaviorsDirty = true
true
}
}
}
@ -191,14 +211,16 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
}
if (isServer) {
if (commandDelay > 0) {
commandDelay -= 1
if (commandDelay == 0) {
queuedCommand.foreach(_())
queuedCommand = None
}
}
api.Network.updateWirelessNetwork(this)
}
if (configCooldown > 0) {
configCooldown -= 1
return
}
var hasPower = getLocalBuffer > 0 || Settings.get.ignorePower
lazy val active = getActiveBehaviors.toIterable // Wrap once.
lazy val activeInputs = configuration.triggers.count(_.isActive)
@ -220,7 +242,7 @@ class ControllerImpl(val player: EntityPlayer) extends Controller with WirelessE
PacketSender.sendNanomachinePower(player)
}
val overload = activeInputs - getSafeInputCount
val overload = activeInputs - getSafeActiveInputs
if (!player.capabilities.isCreativeMode && overload > 0 && player.getEntityWorld.getTotalWorldTime % 20 == 0) {
player.attackEntityFrom(OverloadDamage, overload)
}

View File

@ -698,7 +698,10 @@ object Network extends api.detail.NetworkAPI {
acc + (arg match {
case null | Unit | None => 4
case _: java.lang.Boolean => 4
case _: java.lang.Byte => 4
case _: java.lang.Short => 4
case _: java.lang.Integer => 4
case _: java.lang.Float => 8
case _: java.lang.Double => 8
case value: java.lang.String => value.length max 1
case value: Array[Byte] => value.length max 1