blob: 9bb4c320d337ebbd9aac519bb4b908354935313d [file] [log] [blame] [raw]
package li.cil.oc.server.component
import li.cil.oc.api.network._
import li.cil.oc.common.tileentity.PowerInformation
import li.cil.oc.server.network.Connector
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.{Settings, api}
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
class PowerDistributor(val owner: PowerInformation) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Network).
withComponent("power", Visibility.Network).
create()
var globalBuffer = 0.0
var globalBufferSize = 0.0
private var lastSentRatio = 0.0
private val buffers = mutable.Set.empty[Connector]
private val distributors = mutable.Set.empty[PowerDistributor]
private var dirty = true
// ----------------------------------------------------------------------- //
@LuaCallback(value = "buffer", direct = true)
def buffer(context: Context, args: Arguments): Array[AnyRef] = result(globalBuffer)
@LuaCallback(value = "bufferSize", direct = true)
def bufferSize(context: Context, args: Arguments): Array[AnyRef] = result(globalBufferSize)
// ----------------------------------------------------------------------- //
def changeBuffer(delta: Double): Double = {
if (delta == 0) {
return 0
}
if (Settings.get.ignorePower) {
if (delta < 0) {
return 0
}
else /* if (delta > 0) */ {
return delta
}
}
this.synchronized {
val oldBuffer = globalBuffer
globalBuffer = (globalBuffer + delta) max 0 min globalBufferSize
if (globalBuffer == oldBuffer) {
return delta
}
dirty = true
if (delta < 0) {
var remaining = -delta
for (connector <- buffers) {
if (connector.localBuffer > 0) {
connector.dirty = true
if (connector.localBuffer < remaining) {
remaining -= connector.localBuffer
connector.localBuffer = 0
}
else {
connector.localBuffer -= remaining
return 0
}
}
}
remaining
}
else /* if (delta > 0) */ {
var remaining = delta
for (connector <- buffers) {
if (connector.localBuffer < connector.localBufferSize) {
connector.dirty = true
val space = connector.localBufferSize - connector.localBuffer
if (space < remaining) {
remaining -= space
connector.localBuffer = connector.localBufferSize
}
else {
connector.localBuffer += remaining
return 0
}
}
}
remaining
}
}
}
// ----------------------------------------------------------------------- //
override def update() {
if (node != null && (dirty || buffers.exists(_.dirty))) {
updateCachedValues()
}
}
// ----------------------------------------------------------------------- //
override def onConnect(node: Node) {
super.onConnect(node)
if (node == this.node) {
for (node <- node.reachableNodes) node match {
case connector: Connector if connector.localBufferSize > 0 => this.synchronized {
buffers += connector
globalBuffer += connector.localBuffer
globalBufferSize += connector.localBufferSize
}
case _ => node.host match {
case distributor: PowerDistributor if distributor.node.canBeSeenFrom(this.node) =>
distributors += distributor
case _ =>
}
}
distributors += this
dirty = true
}
else node match {
case connector: Connector if connector.localBufferSize > 0 => this.synchronized {
buffers += connector
globalBuffer += connector.localBuffer
globalBufferSize += connector.localBufferSize
dirty = true
}
case _ => node.host match {
case distributor: PowerDistributor if distributor.node.canBeSeenFrom(this.node) =>
distributors += distributor
case _ =>
}
}
}
override def onDisconnect(node: Node) {
super.onDisconnect(node)
if (node == this.node) this.synchronized {
buffers.clear()
distributors.clear()
globalBuffer = 0
globalBufferSize = 0
}
else node match {
case connector: Connector => this.synchronized {
buffers -= connector
globalBuffer -= connector.localBuffer
globalBufferSize -= connector.localBufferSize
dirty = true
}
case _ => node.host match {
case distributor: PowerDistributor => distributors -= distributor
case _ =>
}
}
}
// ----------------------------------------------------------------------- //
def updateCachedValues() {
// Computer average fill ratio of all buffers.
val (sumBuffer, sumBufferSize) =
buffers.foldRight((0.0, 0.0))((c, acc) => {
c.dirty = false // clear dirty flag for all connectors
(acc._1 + c.localBuffer, acc._2 + c.localBufferSize)
})
// Only send updates if the state changed by more than 5%, more won't be
// noticeable "from the outside" anyway. We send more frequent updates in
// the gui/container of a block that needs it (like robots).
val fillRatio = sumBuffer / sumBufferSize
val shouldSend = (lastSentRatio - fillRatio).abs > (5.0 / 100.0)
for (distributor <- distributors) distributor.synchronized {
distributor.dirty = false
distributor.globalBuffer = sumBuffer
distributor.globalBufferSize = sumBufferSize
distributor.owner.globalBuffer = sumBuffer
distributor.owner.globalBufferSize = sumBufferSize
if (shouldSend) {
distributor.lastSentRatio = fillRatio
ServerPacketSender.sendPowerState(distributor.owner)
}
}
}
}