mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 09:46:53 -04:00
computers need power to run (base level + elapsed cpu time); screens need power to change their display, different costs per operation, depends on number of 'pixels' changed; fixed distributors not balancing buffers if attached to existing network without distributor
This commit is contained in:
parent
301cec06d6
commit
49ced36d5f
@ -7,10 +7,29 @@ object Config {
|
||||
val savePath = "opencomputers/"
|
||||
val scriptPath = "/assets/" + resourceDomain + "/lua/"
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
// Power it takes to run a computer at 100% CPU time for one second.
|
||||
val cpuTimeCost = 256
|
||||
|
||||
// Power it takes to change a single pixel via the set command.
|
||||
val screenSetCost = 1.0 / 20
|
||||
|
||||
// Power it takes to change a single pixel via the fill command.
|
||||
val screenFillCost = 1.0 / 180
|
||||
|
||||
// Power it takes to change a single pixel to blank via the fill command.
|
||||
val screenClearCost = 1.0 / 200
|
||||
|
||||
// Power it takes to move a single pixel via the copy command.
|
||||
val screenCopyCost = 1.0 / 160
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
var blockRenderId = 0
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
@ -4,7 +4,7 @@ import li.cil.oc.api.Persistable
|
||||
import li.cil.oc.api.network.Visibility
|
||||
import li.cil.oc.common.{tileentity, component}
|
||||
import li.cil.oc.util.TextBuffer
|
||||
import li.cil.oc.{util, api}
|
||||
import li.cil.oc.{Config, util, api}
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
|
||||
class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) extends Persistable {
|
||||
@ -36,17 +36,20 @@ class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) exten
|
||||
val (x, truncated) =
|
||||
if (col < 0) (0, s.substring(-col))
|
||||
else (col, s.substring(0, s.length min buffer.width))
|
||||
if (buffer.set(x, row, truncated))
|
||||
owner.onScreenSet(x, row, truncated)
|
||||
if (consumePower(truncated.length, Config.screenSetCost))
|
||||
if (buffer.set(x, row, truncated))
|
||||
owner.onScreenSet(x, row, truncated)
|
||||
}
|
||||
|
||||
def fill(col: Int, row: Int, w: Int, h: Int, c: Char) =
|
||||
if (buffer.fill(col, row, w, h, c))
|
||||
owner.onScreenFill(col, row, w, h, c)
|
||||
if (consumePower(w * h, if (c == ' ') Config.screenClearCost else Config.screenFillCost))
|
||||
if (buffer.fill(col, row, w, h, c))
|
||||
owner.onScreenFill(col, row, w, h, c)
|
||||
|
||||
def copy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) =
|
||||
if (buffer.copy(col, row, w, h, tx, ty))
|
||||
owner.onScreenCopy(col, row, w, h, tx, ty)
|
||||
if (consumePower(w * h, Config.screenCopyCost))
|
||||
if (buffer.copy(col, row, w, h, tx, ty))
|
||||
owner.onScreenCopy(col, row, w, h, tx, ty)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@ -59,6 +62,11 @@ class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) exten
|
||||
buffer.writeToNBT(screenNbt)
|
||||
nbt.setCompoundTag("oc.screen", screenNbt)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private def consumePower(n: Double, cost: Double) =
|
||||
owner.node == null || owner.node.changeBuffer(-n * cost)
|
||||
}
|
||||
|
||||
object Screen {
|
||||
@ -89,16 +97,16 @@ object Screen {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {}
|
||||
|
||||
def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {}
|
||||
|
||||
def onScreenResolutionChange(w: Int, h: Int) {
|
||||
if (node != null)
|
||||
node.sendToReachable("computer.signal", "screen_resized", Int.box(w), Int.box(h))
|
||||
}
|
||||
|
||||
def onScreenSet(col: Int, row: Int, s: String) {}
|
||||
|
||||
def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {}
|
||||
|
||||
def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package li.cil.oc.common.tileentity
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import li.cil.oc.api.Network
|
||||
import li.cil.oc.api.driver.Slot
|
||||
import li.cil.oc.client.{PacketSender => ClientPacketSender}
|
||||
@ -20,7 +19,9 @@ class Computer(isClient: Boolean) extends Rotatable with ComputerEnvironment wit
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private val hasChanged = new AtomicBoolean(true) // For `markChanged`.
|
||||
private var powerConsumed = 0.0
|
||||
|
||||
private var hasChanged = false
|
||||
|
||||
private var isRunning = false
|
||||
|
||||
@ -30,7 +31,10 @@ class Computer(isClient: Boolean) extends Rotatable with ComputerEnvironment wit
|
||||
|
||||
def world = worldObj
|
||||
|
||||
def markAsChanged() = hasChanged.set(true)
|
||||
def markAsChanged(power: Double) = this.synchronized {
|
||||
powerConsumed = (powerConsumed + power) max 0
|
||||
hasChanged = true
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@ -72,12 +76,24 @@ class Computer(isClient: Boolean) extends Rotatable with ComputerEnvironment wit
|
||||
// too, avoiding issues of missing nodes (e.g. in the GPU which would
|
||||
// otherwise loose track of its screen).
|
||||
instance.update()
|
||||
updateRedstoneInput()
|
||||
if (hasChanged.get)
|
||||
val (powerRequired, needsSaving) = this.synchronized {
|
||||
val a = powerConsumed + 0.05
|
||||
val b = hasChanged
|
||||
powerConsumed = 0
|
||||
hasChanged = false
|
||||
(a, b)
|
||||
}
|
||||
if (isRunning && !node.changeBuffer(-powerRequired)) {
|
||||
// TODO try to print to screen? sound effect? particle effect?
|
||||
println("not enough power, shutting down... " + powerRequired)
|
||||
turnOff()
|
||||
}
|
||||
if (needsSaving)
|
||||
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
|
||||
if (isRunning != instance.isRunning)
|
||||
ServerPacketSender.sendComputerState(this, instance.isRunning)
|
||||
isRunning = instance.isRunning
|
||||
updateRedstoneInput()
|
||||
}
|
||||
|
||||
for (component <- components) component match {
|
||||
|
@ -28,10 +28,8 @@ class PowerDistributor extends Rotatable with Environment {
|
||||
if (node != null && node.network == null) {
|
||||
Network.joinOrCreateNetwork(worldObj, xCoord, yCoord, zCoord)
|
||||
}
|
||||
if (!worldObj.isRemote && connectors.exists(_.dirty) && computeAverage()) {
|
||||
// Adjust buffer fill ratio for all buffers to average.
|
||||
connectors.foreach(c => c.buffer = c.bufferSize * average)
|
||||
}
|
||||
if (!worldObj.isRemote && connectors.exists(_.dirty))
|
||||
balance()
|
||||
}
|
||||
|
||||
override def validate() {
|
||||
@ -51,7 +49,7 @@ class PowerDistributor extends Rotatable with Environment {
|
||||
else node match {
|
||||
case connector: Connector =>
|
||||
connectors -= connector
|
||||
computeAverage()
|
||||
balance()
|
||||
case _ => node.host match {
|
||||
case distributor: PowerDistributor => distributors -= distributor
|
||||
case _ =>
|
||||
@ -69,7 +67,7 @@ class PowerDistributor extends Rotatable with Environment {
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
computeAverage()
|
||||
balance()
|
||||
}
|
||||
else node match {
|
||||
case connector: Connector => connectors += connector
|
||||
@ -94,7 +92,7 @@ class PowerDistributor extends Rotatable with Environment {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private def computeAverage() = {
|
||||
private def balance() {
|
||||
// Computer average fill ratio of all buffers.
|
||||
val (minRelativeBuffer, maxRelativeBuffer, sumBuffer, sumBufferSize) =
|
||||
connectors.foldRight((1.0, 0.0, 0.0, 0.0))((c, acc) => {
|
||||
@ -111,6 +109,9 @@ class PowerDistributor extends Rotatable with Environment {
|
||||
ServerPacketSender.sendPowerState(distributor)
|
||||
}
|
||||
}
|
||||
maxRelativeBuffer - minRelativeBuffer > 10e-4
|
||||
if (maxRelativeBuffer - minRelativeBuffer > 10e-4) {
|
||||
// Adjust buffer fill ratio for all buffers to average.
|
||||
connectors.foreach(c => c.buffer = c.bufferSize * average)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,25 +106,27 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def start() = stateMonitor.synchronized(
|
||||
(owner.node.network != null && state == Computer.State.Stopped) && init() && {
|
||||
// Initial state. Will be switched to State.Yielded in the next update()
|
||||
// due to the signals queue not being empty (
|
||||
state = Computer.State.Suspended
|
||||
def start() = stateMonitor.synchronized(owner.node.network != null &&
|
||||
state == Computer.State.Stopped &&
|
||||
owner.installedMemory > 0 &&
|
||||
init() && {
|
||||
// Initial state. Will be switched to State.Yielded in the next update()
|
||||
// due to the signals queue not being empty (
|
||||
state = Computer.State.Suspended
|
||||
|
||||
// Remember when we started, for os.clock().
|
||||
timeStarted = owner.world.getWorldInfo.getWorldTotalTime
|
||||
// Remember when we started, for os.clock().
|
||||
timeStarted = owner.world.getWorldInfo.getWorldTotalTime
|
||||
|
||||
// Mark state change in owner, to send it to clients.
|
||||
owner.markAsChanged()
|
||||
// Mark state change in owner, to send it to clients.
|
||||
owner.markAsChanged(8) // Initial power required to start.
|
||||
|
||||
// Push a dummy signal to get the computer going.
|
||||
signal("dummy")
|
||||
// Push a dummy signal to get the computer going.
|
||||
signal("dummy")
|
||||
|
||||
// All green, computer started successfully.
|
||||
owner.node.sendToReachable("computer.started")
|
||||
true
|
||||
})
|
||||
// All green, computer started successfully.
|
||||
owner.node.sendToReachable("computer.started")
|
||||
true
|
||||
})
|
||||
|
||||
def stop() = stateMonitor.synchronized(state match {
|
||||
case Computer.State.Stopped => false // Nothing to do.
|
||||
@ -925,7 +927,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
sleepUntil = Long.MaxValue
|
||||
|
||||
// Mark state change in owner, to send it to clients.
|
||||
owner.markAsChanged()
|
||||
owner.markAsChanged(Double.NegativeInfinity)
|
||||
})
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
@ -957,6 +959,10 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
} match {
|
||||
case Computer.State.SynchronizedReturn => true
|
||||
case Computer.State.Yielded | Computer.State.Sleeping => false
|
||||
case Computer.State.Stopping =>
|
||||
// stop() was called directly after start(), e.g. due to lack of power.
|
||||
close()
|
||||
return
|
||||
case s =>
|
||||
OpenComputers.log.warning("Running computer from invalid state " + s.toString + ". This is a bug!")
|
||||
close()
|
||||
@ -992,7 +998,8 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
case _ =>
|
||||
lua.resume(1, 0)
|
||||
}
|
||||
cpuTime += System.nanoTime() - cpuStart
|
||||
val runtime = System.nanoTime() - cpuStart
|
||||
cpuTime += runtime
|
||||
|
||||
// Check if this was the first run, meaning the one used for initializing
|
||||
// the kernel (loading the libs, establishing a memory baseline).
|
||||
@ -1051,7 +1058,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
||||
}
|
||||
|
||||
// State has inevitably changed, mark as changed to save again.
|
||||
owner.markAsChanged()
|
||||
owner.markAsChanged(Config.cpuTimeCost.toDouble * runtime / 1000 / 1000 / 1000)
|
||||
}
|
||||
// The kernel thread returned. If it threw we'd we in the catch below.
|
||||
else {
|
||||
@ -1119,8 +1126,10 @@ object Computer {
|
||||
* <p/>
|
||||
* This is called asynchronously from the Computer's executor thread, so the
|
||||
* computer's owner must make sure to handle this in a synchronized fashion.
|
||||
*
|
||||
* @param power the power that should be consumed.
|
||||
*/
|
||||
def markAsChanged(): Unit
|
||||
def markAsChanged(power: Double): Unit
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user