Merge branch 'master-MC1.10' into master-MC1.12

# Conflicts:
#	src/main/resources/assets/opencomputers/lang/en_US.lang
#	src/main/scala/li/cil/oc/Localization.scala
#	src/main/scala/li/cil/oc/common/component/TextBuffer.scala
#	src/main/scala/li/cil/oc/common/init/Items.scala
#	src/main/scala/li/cil/oc/server/component/GraphicsCard.scala
#	src/main/scala/li/cil/oc/server/component/Trade.scala
This commit is contained in:
payonel 2020-05-24 01:40:10 -07:00
commit 012f55a7fa
33 changed files with 558 additions and 320 deletions

View File

@ -244,16 +244,6 @@ opencomputers {
1024
]
# Video ram can be allocated on a gpu. The amount of vram you can allocate
# is equal to the width*height of the max resolution of the gpu multiplied
# by the "vramSize" for that tier. For example, a T2 gpu can have 80*25*2 of
# text buffer space allocated
vramSizes: [
1,
2,
3
]
# This setting allows you to fine-tune how RAM sizes are scaled internally
# on 64 Bit machines (i.e. when the Minecraft server runs in a 64 Bit VM).
# Why is this even necessary? Because objects consume more memory in a 64
@ -1596,4 +1586,20 @@ opencomputers {
# whitelist and the blacklist, the blacklist will win.
dimWhitelist: []
}
# Graphics Card Component Settings
gpu {
# Video ram can be allocated on a gpu. The amount of vram you can allocate
# is equal to the width*height of the max resolution of the gpu multiplied
# by the "vramSize" for that tier. For example, a T2 gpu can have 80*25*2 of
# text buffer space allocated
vramSizes: [ 1, 2, 3 ]
# This setting assigns the budget call cost to invoke bitblt to write vram
# to a screen. Video ram can bitblit to a screen which can cause real life
# network laod the defaults settings put bitblit network impact close to gpu.set
# Increase these values to throttle bitblt more. The cost tier N is bitbltCost * 2^(tier)
# default is .5, which gives: .5, 1, 4
bitbltCost: 0.5
}
}

View File

@ -228,6 +228,7 @@ oc:gui.Rack.None=None
oc:gui.Rack.Right=Right
oc:gui.Rack.Enabled=Enabled
oc:gui.Rack.Disabled=Disabled
oc:gui.Rack.RelayModeTooltip=Relay Mode
oc:gui.Rack.Top=Top
oc:gui.Switch.PacketsPerCycle=Packets / cycle
oc:gui.Switch.QueueSize=Queue size
@ -380,6 +381,7 @@ oc:tooltip.upgradesolargenerator=Can be used to generate energy from sunlight on
oc:tooltip.upgradetank=This upgrade provides a tank for fluid storage for robots and drones. Without one of these, they will not be able to store fluids internally.
oc:tooltip.upgradetankcontroller=This upgrade allows robots and drones more control in how they interact with external tanks, and allows them to transfer fluids into and out of fluid tank items in their inventory.
oc:tooltip.upgradetractorbeam=Equips a device with extremely advanced technology, nicknamed the "Item Magnet". Allows the device to pick up items anywhere within 3 blocks of its location.
oc:tooltip.upgradetrading=Allows robots and drones to trade with villagers.
oc:tooltip.waypoint=Provides a point of reference to devices with a navigation upgrade.
oc:tooltip.wirelessnetworkcard=Allows wireless sending of network messages in addition to normal ones. You can adjust the §fsignal strength§7 to control how far messages are sent. Higher signal strength results in higher energy consumption.
oc:tooltip.worldsensorcard=Allows reading out information about the world, such as its gravity and whether it has a breathable atmosphere. Use results at own risk. The manufacturer takes no responsibility for bodily or material harm caused by decisions made upon the cards' outputs. We have lawyers. And money. Don't even try.

View File

@ -1 +1 @@
{label = "OpenOS", reboot=true, setlabel=true, setboot=true}
{label = "OpenOS", reboot=true, setlabel=true, setboot=true, noclobber={"etc/rc.cfg","home/.shrc"}}

View File

@ -30,7 +30,7 @@ options =
P = options.P,
v = options.v,
x = options.x,
skip = options.skip,
skip = {options.skip},
}
return transfer.batch(args, options)

View File

@ -1,6 +1,4 @@
local computer = require("computer")
local shell = require("shell")
local options
do
@ -12,21 +10,23 @@ do
options = basic(...)
end
if not options then return end
if not options then
return
end
if computer.freeMemory() < 50000 then
print("Low memory, collecting garbage")
for i=1,20 do os.sleep(0) end
for i = 1, 20 do
os.sleep(0)
end
end
local cp, reason = loadfile(shell.resolve("cp", "lua"), "bt", _G)
assert(cp, reason)
local ok, ec = pcall(cp, table.unpack(options.cp_args))
assert(ok, ec)
if ec ~= nil and ec ~= 0 then
return ec
local transfer = require("tools/transfer")
for _, inst in ipairs(options.cp_args) do
local ec = transfer.batch(table.unpack(inst))
if ec ~= nil and ec ~= 0 then
return ec
end
end
print("Installation complete!")
@ -44,7 +44,7 @@ end
if options.reboot then
io.write("Reboot now? [Y/n] ")
if ((io.read() or "n").."y"):match("^%s*[Yy]") then
if ((io.read() or "n") .. "y"):match("^%s*[Yy]") then
print("\nRebooting now!\n")
computer.shutdown(true)
end

View File

@ -24,7 +24,7 @@ options =
i = options.i,
v = options.v,
n = options.n, -- no clobber
skip = options.skip,
skip = {options.skip},
P = true, -- move operations always preserve
r = true, -- move is allowed to move entire dirs
x = true, -- cannot move mount points

View File

@ -32,3 +32,7 @@ for _,line in ipairs(lines) do
io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n")
end
io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n")
if require("filesystem").get("home").isReadOnly() then
io.write("\27[33mNote: Your home directory is readonly. Run `install` and reboot.\27[m\n")
end

View File

@ -20,7 +20,6 @@ shell.setAlias("cls", "clear")
shell.setAlias("rs", "redstone")
shell.setAlias("view", "edit -r")
shell.setAlias("help", "man")
shell.setAlias("cp", "cp -i")
shell.setAlias("l", "ls -lhp")
shell.setAlias("..", "cd ..")
shell.setAlias("df", "df -h")

View File

@ -27,7 +27,7 @@ local utils
local rootfs = fs.get("/")
if not rootfs then
io.stderr:write("no root filesystem, aborting\n");
io.stderr:write("no root filesystem, aborting\n")
os.exit(1)
end
@ -86,11 +86,13 @@ for dev, path in pairs(devices) do
io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n")
os.exit(1)
end
elseif specified or
not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source
not target_filter and
address ~= tmpAddress then
table.insert(targets, {dev=dev, path=install_path, specified=specified})
elseif
specified or
not (source_filter and address:find(source_filter, 1, true) == 1) and -- specified for source
not target_filter and
address ~= tmpAddress
then
table.insert(targets, {dev = dev, path = install_path, specified = specified})
end
end
@ -105,11 +107,11 @@ for dev, path in pairs(devices) do
local install_path = dev == source_filter_dev and options.from or path
local specified = source_filter and address:find(source_filter, 1, true) == 1
if fs.list(install_path)()
and (specified or
not source_filter and
address ~= tmpAddress and
not (address == rootfs.address and not rootfs.isReadOnly())) then
if
fs.list(install_path)() and
(specified or
not source_filter and address ~= tmpAddress and not (address == rootfs.address and not rootfs.isReadOnly()))
then
local prop = {}
local prop_path = install_path .. "/.prop"
local prop_file = fs.open(prop_path)
@ -125,7 +127,7 @@ for dev, path in pairs(devices) do
end
if not prop.ignore then
if not label or label:lower() == (prop.label or dev.getLabel() or ""):lower() then
table.insert(sources, {dev=dev, path=install_path, prop=prop, specified=specified})
table.insert(sources, {dev = dev, path = install_path, prop = prop, specified = specified})
end
end
end
@ -137,23 +139,24 @@ if #sources ~= 1 then
utils = loadfile(utils_path, "bt", _G)
source = utils("select", "sources", options, sources)
end
if not source then return end
if not source then
return
end
options =
{
from = source.path .. '/',
fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""),
root = fs.canonical(options.root or options.toDir or source.prop.root or ""),
update = options.update or options.u,
label = source.prop.label or label,
options = {
from = source.path .. "/",
fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""),
root = fs.canonical(options.root or options.toDir or source.prop.root or ""),
update = options.update or options.u,
label = source.prop.label or label,
setlabel = not (options.nosetlabel or options.nolabelset) and source.prop.setlabel,
setboot = not (options.nosetboot or options.noboot) and source.prop.setboot,
reboot = not options.noreboot and source.prop.reboot,
setboot = not (options.nosetboot or options.noboot) and source.prop.setboot,
reboot = not options.noreboot and source.prop.reboot
}
local source_display = options.label or source.dev.getLabel() or source.path
-- Remove the source from the target options
for index,entry in ipairs(targets) do
for index, entry in ipairs(targets) do
if entry.dev == source.dev then
table.remove(targets, index)
target = targets[1]
@ -162,47 +165,67 @@ end
-- Ask the user to select a target
if #targets ~= 1 then
if #sources == 1 then
io.write(source_display, " selected for install\n")
end
if #sources == 1 then
io.write(source_display, " selected for install\n")
end
utils = utils or loadfile(utils_path, "bt", _G)
utils = utils or loadfile(utils_path, "bt", _G)
target = utils("select", "targets", options, targets)
end
if not target then return end
if not target then
return
end
options.to = target.path .. '/'
options.to = target.path .. "/"
local cp_args =
{
"-vrx" .. (options.update and "ui" or ""),
"--skip=.prop",
fs.concat(options.from, options.fromDir) .. "/.",
fs.concat(options.to , options.root)
local function resolveFrom(path)
return fs.concat(options.from, options.fromDir) .. "/" .. path
end
local fullTargetPath = fs.concat(options.to, options.root)
local transfer_args = {
{
{resolveFrom("."), fullTargetPath},
{
cmd = "cp",
r = true, v = true, x = true, u = options.update, i = options.update,
skip = {resolveFrom(".prop")},
}
}
}
if source.prop.noclobber and #source.prop.noclobber > 0 then
local noclobber_opts = {cmd = "cp", v = true, n = true}
for _, noclobber in ipairs(source.prop.noclobber or {}) do
local noclobberFrom = resolveFrom(noclobber)
local noclobberTo = fs.concat(fullTargetPath, noclobber)
table.insert(transfer_args[1][2].skip, noclobberFrom)
table.insert(transfer_args, {{noclobberFrom, noclobberTo}, noclobber_opts})
end
end
local special_target = ""
if #targets > 1 or target_filter or source_filter then
special_target = " to " .. cp_args[4]
special_target = " to " .. transfer_args[1][1][2]
end
io.write("Install " .. source_display .. special_target .. "? [Y/n] ")
if not ((io.read() or "n").."y"):match("^%s*[Yy]") then
if not ((io.read() or "n") .. "y"):match("^%s*[Yy]") then
io.write("Installation cancelled\n")
os.exit()
end
local installer_path = options.from .. "/.install"
if fs.exists(installer_path) then
local installer, reason = loadfile(installer_path, "bt", setmetatable({install=options}, {__index = _G}))
local installer, reason = loadfile(installer_path, "bt", setmetatable({install = options}, {__index = _G}))
if not installer then
io.stderr:write("installer failed to load: " .. tostring(reason) .. '\n')
io.stderr:write("installer failed to load: " .. tostring(reason) .. "\n")
os.exit(1)
end
os.exit(installer())
end
options.cp_args = cp_args
options.cp_args = transfer_args
options.target = target
return options

View File

@ -116,15 +116,12 @@ while term.isAvailable() do
else
local ok, why = pcall(function()
for i = 2, result.n do
io.write(require("serialization").serialize(result[i], true) .. "\t")
io.write(require("serialization").serialize(result[i], true), i < result.n and "\t" or "\n")
end
end)
if not ok then
io.stderr:write("crashed serializing result: ", tostring(why))
end
if term.getCursor() > 1 then
io.write("\n")
end
end
else
io.stderr:write(tostring(reason) .. "\n")

View File

@ -93,7 +93,7 @@ function lib.recurse(fromPath, toPath, options, origin, top)
local toPathFull = shell.resolve(toPath)
local mv = options.cmd == "mv"
local verbose = options.v and (not mv or top)
if select(2, fromPathFull:find(options.skip)) == #fromPathFull then
if options.skip[fromPathFull] then
status(verbose, string.format("skipping %s", fromPath))
return true
end
@ -221,8 +221,13 @@ function lib.batch(args, options)
-- standardized options
options.i = options.i and not options.f
options.P = options.P or options.r
options.skip = text.escapeMagic(options.skip or "")
local skips = options.skip or {}
options.skip = {}
for _, skip_item in ipairs(skips) do
options.skip[shell.resolve(skip_item)] = true
end
local origin = {}
for dev,path in fs.mounts() do
origin[path] = dev

View File

@ -796,7 +796,7 @@ sandbox = {
local handled = false
checkArg(2, msgh, "function")
local result = table.pack(xpcall(f, function(...)
if (...) == tooLongWithoutYielding then
if rawequal((...), tooLongWithoutYielding) then
return tooLongWithoutYielding
elseif handled then
return ...
@ -805,7 +805,7 @@ sandbox = {
return msgh(...)
end
end, ...))
if result[2] == tooLongWithoutYielding then
if rawequal(result[2], tooLongWithoutYielding) then
result = table.pack(result[1], select(2, pcallTimeoutCheck(pcall(msgh, tostring(tooLongWithoutYielding)))))
end
return table.unpack(result, 1, result.n)

View File

@ -121,6 +121,7 @@ Rinzler # Tron
Twiki # Buck Rodgers
Uniblab # The Jetsons
Unimate # First programmable robot.
VEGA # Doom 2016
Vertigo # Perry Rhodan
Vexatos # Contributor
V.I.K.I. # Virtual Interactive Kinetic Intelligence - I, Robot

View File

@ -143,6 +143,8 @@ object Localization {
def RelayEnabled: String = localizeImmediately("gui.Rack.Enabled")
def RelayDisabled: String = localizeImmediately("gui.Rack.Disabled")
def RelayModeTooltip: String = localizeImmediately("gui.Rack.RelayModeTooltip")
}
object Switch {

View File

@ -92,12 +92,6 @@ class Settings(val config: Config) {
OpenComputers.log.warn("Bad number of RAM sizes, ignoring.")
Array(192, 256, 384, 512, 768, 1024)
}
val vramSizes = Array(config.getIntList("computer.lua.vramSizes"): _*) match {
case Array(tier1, tier2, tier3) => Array(tier1: Int, tier2: Int, tier3: Int)
case _ =>
OpenComputers.log.warn("Bad number of VRAM sizes, ignoring.")
Array(1, 2, 3)
}
val ramScaleFor64Bit = config.getDouble("computer.lua.ramScaleFor64Bit") max 1
val maxTotalRam = config.getInt("computer.lua.maxTotalRam") max 0
@ -476,24 +470,34 @@ class Settings(val config: Config) {
// >= 1.7.4
val maxSignalQueueSize: Int = (if (config.hasPath("computer.maxSignalQueueSize")) config.getInt("computer.maxSignalQueueSize") else 256) min 256
// >= 1.7.6
val vramSizes: Array[Double] = Array(config.getDoubleList("gpu.vramSizes"): _*) match {
case Array(tier1, tier2, tier3) => Array(tier1: Double, tier2: Double, tier3: Double)
case _ =>
OpenComputers.log.warn("Bad number of VRAM sizes (expected 3), ignoring.")
Array(1, 2, 3)
}
val bitbltCost: Double = if (config.hasPath("gpu.bitbltCost")) config.getDouble("gpu.bitbltCost") else 0.5
}
object Settings {
val resourceDomain = "opencomputers"
val namespace = "oc:"
val savePath = "opencomputers/"
val scriptPath = "/assets/" + resourceDomain + "/lua/"
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
val screenDepthsByTier = Array(api.internal.TextBuffer.ColorDepth.OneBit, api.internal.TextBuffer.ColorDepth.FourBit, api.internal.TextBuffer.ColorDepth.EightBit)
val deviceComplexityByTier = Array(12, 24, 32, 9001)
val scriptPath: String = "/assets/" + resourceDomain + "/lua/"
val screenResolutionsByTier: Array[(Int, Int)] = Array((50, 16), (80, 25), (160, 50))
val screenDepthsByTier: Array[api.internal.TextBuffer.ColorDepth] = Array(api.internal.TextBuffer.ColorDepth.OneBit, api.internal.TextBuffer.ColorDepth.FourBit, api.internal.TextBuffer.ColorDepth.EightBit)
val deviceComplexityByTier: Array[Int] = Array(12, 24, 32, 9001)
var rTreeDebugRenderer = false
var blockRenderId = -1
var blockRenderId: Int = -1
def basicScreenPixels = screenResolutionsByTier(0)._1 * screenResolutionsByTier(0)._2
def basicScreenPixels: Int = screenResolutionsByTier(0)._1 * screenResolutionsByTier(0)._2
private var settings: Settings = _
def get = settings
def get: Settings = settings
def load(file: File) = {
import scala.compat.Platform.EOL

View File

@ -733,13 +733,11 @@ object PacketHandler extends CommonPacketHandler {
}
def onTextBufferRamInit(p: PacketParser, buffer: api.internal.TextBuffer): Unit = {
val owner = p.readUTF()
val id = p.readInt()
val nbt = p.readNBT()
buffer match {
case screen: component.traits.VideoRamAware => screen.loadBuffer(id, nbt)
case _ => // ignore
}
component.ClientGpuTextBufferHandler.loadBuffer(buffer, owner, id, nbt)
}
def onTextBufferBitBlt(p: PacketParser, buffer: api.internal.TextBuffer): Unit = {
@ -747,24 +745,19 @@ object PacketHandler extends CommonPacketHandler {
val row = p.readInt()
val w = p.readInt()
val h = p.readInt()
val owner = p.readUTF()
val id = p.readInt()
val fromCol = p.readInt()
val fromRow = p.readInt()
component.GpuTextBuffer.bitblt(buffer, col, row, w, h, id, fromCol, fromRow)
component.ClientGpuTextBufferHandler.bitblt(buffer, col, row, w, h, owner, id, fromCol, fromRow)
}
def onTextBufferRamDestroy(p: PacketParser, buffer: api.internal.TextBuffer): Unit = {
val length = p.readInt()
val ids = new Array[Int](length)
for (i <- 0 until length) {
ids(i) = p.readInt()
}
val owner = p.readUTF()
val id = p.readInt()
buffer match {
case screen: component.traits.VideoRamAware => screen.removeBuffers(ids)
case _ => // ignore, not compatible with bitblts
}
component.ClientGpuTextBufferHandler.removeBuffer(buffer, owner, id)
}
def onTextBufferMultiRawSetText(p: PacketParser, buffer: api.internal.TextBuffer) {

View File

@ -14,6 +14,8 @@ import net.minecraft.entity.player.InventoryPlayer
import net.minecraft.util.EnumFacing
import org.lwjgl.opengl.GL11
import scala.collection.convert.WrapAsJava.asJavaCollection
class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends DynamicGuiContainer(new container.Rack(playerInventory, rack)) {
ySize = 210
@ -251,6 +253,12 @@ class Rack(playerInventory: InventoryPlayer, val rack: tileentity.Rack) extends
x, y, 0x404040)
}
if (relayButton.isMouseOver) {
val tooltip = new java.util.ArrayList[String]
tooltip.addAll(asJavaCollection(Localization.Rack.RelayModeTooltip.lines.toIterable))
copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRenderer)
}
RenderState.popAttrib()
}

View File

@ -1,14 +1,24 @@
package li.cil.oc.common.component
import li.cil.oc.api.network.{ManagedEnvironment, Message, Node}
import java.io.InvalidObjectException
import java.security.InvalidParameterException
import li.cil.oc.api.network.{Environment, Message, Node}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound
import li.cil.oc.api.internal.TextBuffer.ColorDepth
import li.cil.oc.api
import li.cil.oc.common.component.traits.TextBufferProxy
import li.cil.oc.util.PackedColor
import li.cil.oc.common.component.traits.{TextBufferProxy, VideoRamDevice, VideoRamRasterizer}
class GpuTextBuffer(val owner: String, val id: Int, val data: li.cil.oc.util.TextBuffer) extends traits.TextBufferProxy {
// the gpu ram does not join nor is searchable to the network
// this field is required because the api TextBuffer is an Environment
override def node(): Node = {
throw new InvalidObjectException("GpuTextBuffers do not have nodes")
}
class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends ManagedEnvironment with traits.TextBufferProxy {
override def getMaximumWidth: Int = data.width
override def getMaximumHeight: Int = data.height
override def getViewportWidth: Int = data.height
@ -17,10 +27,19 @@ class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends Ma
var dirty: Boolean = true
override def onBufferSet(col: Int, row: Int, s: String, vertical: Boolean): Unit = dirty = true
override def onBufferColorChange(): Unit = dirty = true
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = dirty = true
override def onBufferCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int): Unit = dirty = true
override def onBufferFill(col: Int, row: Int, w: Int, h: Int, c: Char): Unit = dirty = true
override def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = dirty = false
override def load(nbt: NBTTagCompound): Unit = {
// the data is initially dirty because other devices don't know about it yet
data.load(nbt)
dirty = true
}
override def save(nbt: NBTTagCompound): Unit = {
data.save(nbt)
dirty = false
}
override def setEnergyCostPerTick(value: Double): Unit = {}
override def getEnergyCostPerTick: Double = 0
@ -47,29 +66,42 @@ class GpuTextBuffer(val id: Int, val data: li.cil.oc.util.TextBuffer) extends Ma
override def mouseScroll(x: Double, y: Double, delta: Int, player: EntityPlayer): Unit = {}
override def canUpdate: Boolean = false
override def update(): Unit = {}
override def node: li.cil.oc.api.network.Node = null
override def onConnect(node: Node): Unit = {}
override def onDisconnect(node: Node): Unit = {}
override def onMessage(message: Message): Unit = {}
override def load(nbt: NBTTagCompound): Unit = {}
override def save(nbt: NBTTagCompound): Unit = {}
}
object GpuTextBuffer {
def wrap(id: Int, data: li.cil.oc.util.TextBuffer): GpuTextBuffer = new GpuTextBuffer(id, data)
def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, srcId: Int, fromCol: Int, fromRow: Int): Unit = {
object ClientGpuTextBufferHandler {
def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, owner: String, srcId: Int, fromCol: Int, fromRow: Int): Unit = {
dst match {
case screen: traits.TextBufferProxy => screen.getBuffer(srcId) match {
case videoDevice: VideoRamRasterizer => videoDevice.getBuffer(owner, srcId) match {
case Some(buffer: GpuTextBuffer) => {
bitblt(dst, col, row, w, h, buffer, fromCol, fromRow)
GpuTextBuffer.bitblt(dst, col, row, w, h, buffer, fromCol, fromRow)
}
case _ => // ignore - got a bitblt for a missing buffer
}
case _ => // ignore - weird packet handler called this, should only happen for screens that know about thsi
case _ => // ignore - weird packet handler called this, should only happen for video ram aware devices
}
}
def removeBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int): Boolean = {
buffer match {
case screen: VideoRamRasterizer => screen.removeBuffer(owner, id)
case _ => false // ignore, not compatible with bitblts
}
}
def loadBuffer(buffer: api.internal.TextBuffer, owner: String, id: Int, nbt: NBTTagCompound): Boolean = {
buffer match {
case screen: VideoRamRasterizer => screen.loadBuffer(owner, id, nbt)
case _ => false // ignore, not compatible with bitblts
}
}
}
object GpuTextBuffer {
def wrap(owner: String, id: Int, data: li.cil.oc.util.TextBuffer): GpuTextBuffer = new GpuTextBuffer(owner, id, data)
def bitblt(dst: api.internal.TextBuffer, col: Int, row: Int, w: Int, h: Int, src: api.internal.TextBuffer, fromCol: Int, fromRow: Int): Unit = {
val x = col - 1
val y = row - 1
@ -118,37 +150,28 @@ object GpuTextBuffer {
}
dst match {
case dstRam: GpuTextBuffer => src match {
case srcRam: GpuTextBuffer => write_vram_to_vram(dstRam, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcRam, adjustedSourceX, adjustedSourceY)
case srcScreen: traits.TextBufferProxy => write_screen_to_vram(dstRam, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcScreen, adjustedSourceX, adjustedSourceY)
case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations")
case dstScreen: TextBuffer => src match {
case srcGpu: GpuTextBuffer => write_vram_to_screen(dstScreen, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcGpu, adjustedSourceX, adjustedSourceY)
case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations to a screen")
}
case dstScreen: traits.TextBufferProxy => src match {
case srcRam: GpuTextBuffer => write_vram_to_screen(dstScreen, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcRam, adjustedSourceX, adjustedSourceY)
case _: traits.TextBufferProxy => throw new UnsupportedOperationException("Screen to screen bitblt not supported")
case dstGpu: GpuTextBuffer => src match {
case srcProxy: TextBufferProxy => write_to_vram(dstGpu, adjustedDstX, adjustedDstY, adjustedWidth, adjustedHeight, srcProxy, adjustedSourceX, adjustedSourceY)
case _ => throw new UnsupportedOperationException("Source buffer does not support bitblt operations")
}
case _ => throw new UnsupportedOperationException("Destination buffer does not support bitblt operations")
}
}
def write_vram_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = {
dstRam.data.rawcopy(x + 1, y + 1, w, h, srcRam.data, fx + 1, fx + 1)
}
def write_vram_to_screen(dstScreen: traits.TextBufferProxy, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = {
def write_vram_to_screen(dstScreen: TextBuffer, x: Int, y: Int, w: Int, h: Int, srcRam: GpuTextBuffer, fx: Int, fy: Int): Boolean = {
if (dstScreen.data.rawcopy(x + 1, y + 1, w, h, srcRam.data, fx + 1, fy + 1)) {
// rawcopy returns true only if data was modified
dstScreen.addBuffer(srcRam)
dstScreen.onBufferBitBlt(x + 1, y + 1, w, h, srcRam.id, fx + 1, fy + 1)
dstScreen.onBufferBitBlt(x + 1, y + 1, w, h, srcRam, fx + 1, fy + 1)
true
} else false
}
def write_screen_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, srcScreen: traits.TextBufferProxy, fx: Int, fy: Int): Boolean = {
val format: PackedColor.ColorFormat = PackedColor.Depth.format(srcScreen.getColorDepth)
val tempGpu = GpuTextBuffer.wrap(id = -1, new li.cil.oc.util.TextBuffer(w, h, format))
tempGpu.data.rawcopy(col = 1, row = 1, w, h, srcScreen.data, fx + 1, fy + 1)
write_vram_to_vram(dstRam, x, y, w, h, tempGpu, fx = 0, fy = 0)
def write_to_vram(dstRam: GpuTextBuffer, x: Int, y: Int, w: Int, h: Int, src: TextBufferProxy, fx: Int, fy: Int): Boolean = {
dstRam.data.rawcopy(x + 1, y + 1, w, h, src.data, fx + 1, fy + 1)
}
}

View File

@ -22,6 +22,7 @@ import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common._
import li.cil.oc.common.item.data.NodeData
import li.cil.oc.common.component.traits.TextBufferProxy
import li.cil.oc.common.component.traits.VideoRamRasterizer
import li.cil.oc.server.component.Keyboard
import li.cil.oc.server.{ComponentTracker => ServerComponentTracker}
import li.cil.oc.server.{PacketSender => ServerPacketSender}
@ -43,7 +44,7 @@ import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.TextBufferProxy with DeviceInfo {
class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.TextBufferProxy with VideoRamRasterizer with DeviceInfo {
override val node = api.Network.newNode(this, Visibility.Network).
withComponent("screen").
withConnector().
@ -326,16 +327,16 @@ class TextBuffer(val host: EnvironmentHost) extends AbstractManagedEnvironment w
proxy.onBufferSet(col, row, s, vertical)
}
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {
proxy.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow)
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = {
proxy.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow)
}
override def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = {
proxy.onBufferRamInit(id, ram)
override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = {
proxy.onBufferRamInit(ram)
}
override def onBufferRamDestroy(ids: Array[Int]): Unit = {
proxy.onBufferRamDestroy(ids)
override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = {
proxy.onBufferRamDestroy(ram)
}
override def rawSetText(col: Int, row: Int, text: Array[Array[Char]]): Unit = {
@ -568,15 +569,15 @@ object TextBuffer {
owner.relativeLitArea = -1
}
def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {
def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = {
owner.relativeLitArea = -1
}
def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = {
def onBufferRamInit(ram: component.GpuTextBuffer): Unit = {
owner.relativeLitArea = -1
}
def onBufferRamDestroy(ids: Array[Int]): Unit = {
def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = {
owner.relativeLitArea = -1
}
@ -663,17 +664,17 @@ object TextBuffer {
markDirty()
}
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {
super.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow)
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = {
super.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow)
markDirty()
}
override def onBufferRamInit(id: Int, buffer: TextBufferProxy): Unit = {
super.onBufferRamInit(id, buffer)
override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = {
super.onBufferRamInit(ram)
}
override def onBufferRamDestroy(ids: Array[Int]): Unit = {
super.onBufferRamDestroy(ids)
override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = {
super.onBufferRamDestroy(ram)
}
override def keyDown(character: Char, code: Int, player: EntityPlayer) {
@ -778,24 +779,24 @@ object TextBuffer {
owner.synchronized(ServerPacketSender.appendTextBufferSet(owner.pendingCommands, col, row, s, vertical))
}
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {
super.onBufferBitBlt(col, row, w, h, id, fromCol, fromRow)
override def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit = {
super.onBufferBitBlt(col, row, w, h, ram, fromCol, fromRow)
owner.host.markChanged()
owner.synchronized(ServerPacketSender.appendTextBufferBitBlt(owner.pendingCommands, col, row, w, h, id, fromCol, fromRow))
owner.synchronized(ServerPacketSender.appendTextBufferBitBlt(owner.pendingCommands, col, row, w, h, ram.owner, ram.id, fromCol, fromRow))
}
override def onBufferRamInit(id: Int, buffer: TextBufferProxy): Unit = {
super.onBufferRamInit(id, buffer)
override def onBufferRamInit(ram: component.GpuTextBuffer): Unit = {
super.onBufferRamInit(ram)
owner.host.markChanged()
val nbt = new NBTTagCompound()
buffer.data.save(nbt)
owner.synchronized(ServerPacketSender.appendTextBufferRamInit(owner.pendingCommands, id, nbt))
ram.save(nbt)
owner.synchronized(ServerPacketSender.appendTextBufferRamInit(owner.pendingCommands, ram.owner, ram.id, nbt))
}
override def onBufferRamDestroy(ids: Array[Int]): Unit = {
super.onBufferRamDestroy(ids)
override def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit = {
super.onBufferRamDestroy(ram)
owner.host.markChanged()
owner.synchronized(ServerPacketSender.appendTextBufferRamDestroy(owner.pendingCommands, ids))
owner.synchronized(ServerPacketSender.appendTextBufferRamDestroy(owner.pendingCommands, ram.owner, ram.id))
}
override def onBufferRawSetText(col: Int, row: Int, text: Array[Array[Char]]) {

View File

@ -5,7 +5,7 @@ import li.cil.oc.api
import li.cil.oc.api.internal.TextBuffer
import li.cil.oc.util.PackedColor
trait TextBufferProxy extends api.internal.TextBuffer with VideoRamAware {
trait TextBufferProxy extends api.internal.TextBuffer {
def data: util.TextBuffer
override def getWidth: Int = data.width

View File

@ -1,70 +0,0 @@
package li.cil.oc.common.component.traits
import li.cil.oc.common.component
import net.minecraft.nbt.NBTTagCompound
trait VideoRamAware {
private val internalBuffers = new scala.collection.mutable.HashMap[Int, component.GpuTextBuffer]
val RESERVED_SCREEN_INDEX: Int = 0
def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {}
def onBufferRamInit(id: Int, ram: TextBufferProxy): Unit = {}
def onBufferRamDestroy(ids: Array[Int]): Unit = {}
def bufferIndexes(): Array[Int] = internalBuffers.collect {
case (index: Int, _: Any) => index
}.toArray
def addBuffer(buffer: component.GpuTextBuffer): Boolean = {
val preexists = internalBuffers.contains(buffer.id)
internalBuffers += buffer.id -> buffer
if (!preexists || buffer.dirty) {
buffer.onBufferRamInit(buffer.id, buffer)
onBufferRamInit(buffer.id, buffer)
}
preexists
}
def removeBuffers(ids: Array[Int]): Boolean = {
var allRemoved: Boolean = true
if (ids.nonEmpty) {
onBufferRamDestroy(ids)
for (id <- ids) {
if (internalBuffers.remove(id).isEmpty)
allRemoved = false
}
}
allRemoved
}
def removeAllBuffers(): Boolean = removeBuffers(bufferIndexes())
def loadBuffer(id: Int, nbt: NBTTagCompound): Unit = {
val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat)
src.load(nbt)
addBuffer(component.GpuTextBuffer.wrap(id, src))
}
def getBuffer(id: Int): Option[component.GpuTextBuffer] = {
if (internalBuffers.contains(id))
Option(internalBuffers(id))
else
None
}
def nextAvailableBufferIndex: Int = {
var index = RESERVED_SCREEN_INDEX + 1
while (internalBuffers.contains(index)) {
index += 1;
}
index
}
def calculateUsedMemory(): Int = {
var sum: Int = 0
for ((_, buffer: component.GpuTextBuffer) <- internalBuffers) {
sum += buffer.data.width * buffer.data.height
}
sum
}
}

View File

@ -0,0 +1,68 @@
package li.cil.oc.common.component.traits
import li.cil.oc.common.component
import net.minecraft.nbt.NBTTagCompound
import scala.collection.mutable
trait VideoRamDevice {
private val internalBuffers = new mutable.HashMap[Int, component.GpuTextBuffer]
val RESERVED_SCREEN_INDEX: Int = 0
def isEmpty: Boolean = internalBuffers.isEmpty
def onBufferRamDestroy(id: Int): Unit = {}
def bufferIndexes(): Array[Int] = internalBuffers.collect {
case (index: Int, _: Any) => index
}.toArray
def addBuffer(ram: component.GpuTextBuffer): Boolean = {
val preexists = internalBuffers.contains(ram.id)
internalBuffers += ram.id -> ram
preexists
}
def removeBuffers(ids: Array[Int]): Int = {
var count = 0
if (ids.nonEmpty) {
for (id <- ids) {
if (internalBuffers.remove(id).nonEmpty) {
onBufferRamDestroy(id)
count += 1
}
}
}
count
}
def removeAllBuffers(): Int = removeBuffers(bufferIndexes())
def loadBuffer(address: String, id: Int, nbt: NBTTagCompound): Unit = {
val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat)
src.load(nbt)
addBuffer(component.GpuTextBuffer.wrap(address, id, src))
}
def getBuffer(id: Int): Option[component.GpuTextBuffer] = {
if (internalBuffers.contains(id))
Option(internalBuffers(id))
else
None
}
def nextAvailableBufferIndex: Int = {
var index = RESERVED_SCREEN_INDEX + 1
while (internalBuffers.contains(index)) {
index += 1;
}
index
}
def calculateUsedMemory(): Int = {
var sum: Int = 0
for ((_, buffer: component.GpuTextBuffer) <- internalBuffers) {
sum += buffer.data.width * buffer.data.height
}
sum
}
}

View File

@ -0,0 +1,82 @@
package li.cil.oc.common.component.traits
import li.cil.oc.common.component
import li.cil.oc.common.component.GpuTextBuffer
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.village.VillageDoorInfo
import scala.collection.mutable
trait VideoRamRasterizer {
class VirtualRamDevice(val owner: String) extends VideoRamDevice {}
private val internalBuffers = new mutable.HashMap[String, VideoRamDevice]
def onBufferRamInit(ram: component.GpuTextBuffer): Unit
def onBufferBitBlt(col: Int, row: Int, w: Int, h: Int, ram: component.GpuTextBuffer, fromCol: Int, fromRow: Int): Unit
def onBufferRamDestroy(ram: component.GpuTextBuffer): Unit
def addBuffer(ram: GpuTextBuffer): Boolean = {
var gpu = internalBuffers.get(ram.owner)
if (gpu.isEmpty) {
gpu = Option(new VirtualRamDevice(ram.owner))
internalBuffers += ram.owner -> gpu.get
}
val preexists: Boolean = gpu.get.addBuffer(ram)
if (!preexists || ram.dirty) {
onBufferRamInit(ram)
}
preexists
}
def removeBuffer(owner: String, id: Int): Boolean = {
internalBuffers.get(owner) match {
case Some(gpu: VideoRamDevice) => {
gpu.getBuffer(id) match {
case Some(ram: component.GpuTextBuffer) => {
onBufferRamDestroy(ram)
gpu.removeBuffers(Array(id)) == 1
}
case _ => false
}
}
case _ => false
}
}
def removeAllBuffers(owner: String): Int = {
var count = 0
internalBuffers.get(owner) match {
case Some(gpu: VideoRamDevice) => {
val ids = gpu.bufferIndexes()
for (id <- ids) {
if (removeBuffer(owner, id)) {
count += 1
}
}
}
case _ => Unit
}
count
}
def removeAllBuffers(): Int = {
var count = 0
for ((owner: String, _: Any) <- internalBuffers) {
count += removeAllBuffers(owner)
}
count
}
def loadBuffer(owner: String, id: Int, nbt: NBTTagCompound): Boolean = {
val src = new li.cil.oc.util.TextBuffer(width = 1, height = 1, li.cil.oc.util.PackedColor.SingleBitFormat)
src.load(nbt)
addBuffer(component.GpuTextBuffer.wrap(owner, id, src))
}
def getBuffer(owner: String, id: Int): Option[component.GpuTextBuffer] = {
internalBuffers.get(owner) match {
case Some(gpu: VideoRamDevice) => gpu.getBuffer(id)
case _ => None
}
}
}

View File

@ -261,6 +261,8 @@ object Items extends ItemAPI {
safeGetStack(Constants.ItemName.CraftingUpgrade),
safeGetStack(Constants.ItemName.HoverUpgradeTier2),
safeGetStack(Constants.ItemName.AngelUpgrade),
safeGetStack(Constants.ItemName.TradingUpgrade),
safeGetStack(Constants.ItemName.ExperienceUpgrade),
safeGetStack(Constants.ItemName.GraphicsCardTier3),
safeGetStack(Constants.ItemName.RedstoneCardTier2),

View File

@ -1,3 +1,5 @@
package li.cil.oc.common.item
class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier
class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier {
override protected def tooltipName: Option[String] = Option(super.unlocalizedName)
}

View File

@ -32,7 +32,7 @@ import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalancer with traits.ComponentInventory with traits.Rotatable with traits.BundledRedstoneAware with Analyzable with internal.Rack with traits.StateAware {
var isRelayEnabled = true
var isRelayEnabled = false
val lastData = new Array[NBTTagCompound](getSizeInventory)
val hasChanged: Array[Boolean] = Array.fill(getSizeInventory)(true)

View File

@ -670,31 +670,31 @@ object PacketSender {
pb.writeBoolean(vertical)
}
def appendTextBufferBitBlt(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, id: Int, fromCol: Int, fromRow: Int): Unit = {
def appendTextBufferBitBlt(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, owner: String, id: Int, fromCol: Int, fromRow: Int): Unit = {
pb.writePacketType(PacketType.TextBufferBitBlt)
pb.writeInt(col)
pb.writeInt(row)
pb.writeInt(w)
pb.writeInt(h)
pb.writeUTF(owner)
pb.writeInt(id)
pb.writeInt(fromCol)
pb.writeInt(fromRow)
}
def appendTextBufferRamInit(pb: PacketBuilder, id: Int, nbt: NBTTagCompound): Unit = {
def appendTextBufferRamInit(pb: PacketBuilder, address: String, id: Int, nbt: NBTTagCompound): Unit = {
pb.writePacketType(PacketType.TextBufferRamInit)
pb.writeUTF(address)
pb.writeInt(id)
pb.writeNBT(nbt)
}
def appendTextBufferRamDestroy(pb: PacketBuilder, ids: Array[Int]): Unit = {
def appendTextBufferRamDestroy(pb: PacketBuilder, owner: String, id: Int): Unit = {
pb.writePacketType(PacketType.TextBufferRamDestroy)
pb.writeInt(ids.length)
for (idx <- ids) {
pb.writeInt(idx)
}
pb.writeUTF(owner)
pb.writeInt(id)
}
def appendTextBufferRawSetText(pb: PacketBuilder, col: Int, row: Int, text: Array[Array[Char]]) {

View File

@ -211,7 +211,7 @@ class DebugCard(host: EnvironmentHost) extends AbstractManagedEnvironment with D
}
}
@Callback(doc = """function(x:number, y:number, z:number):boolean -- Connect the debug card to the block at the specified coordinates.""")
@Callback(doc = """function(x:number, y:number, z:number):boolean -- Add a component block at the specified coordinates to the computer network.""")
def connectToBlock(context: Context, args: Arguments): Array[AnyRef] = {
checkAccess()
val x = args.checkInteger(0)
@ -821,7 +821,7 @@ object DebugCard {
}
val count = args.checkInteger(1)
val damage = args.checkInteger(2)
val tagJson = args.checkString(3)
val tagJson = args.optString(3, "")
val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.getTagFromJson(tagJson)
val position = BlockPosition(args.checkDouble(4), args.checkDouble(5), args.checkDouble(6), world)
val side = args.checkSideAny(7)

View File

@ -7,9 +7,7 @@ import li.cil.oc.api.Network
import li.cil.oc.api.driver.DeviceInfo
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
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.machine.{Arguments, Callback, Context, LimitReachedException}
import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.api.prefab.AbstractManagedEnvironment
@ -33,7 +31,7 @@ import scala.util.matching.Regex
// saved, but before the computer was saved, leading to mismatching states in
// the save file - a Bad Thing (TM).
class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with DeviceInfo with component.traits.VideoRamAware {
class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with DeviceInfo with component.traits.VideoRamDevice {
override val node = Network.newNode(this, Visibility.Neighbors).
withComponent("gpu").
withConnector().
@ -71,8 +69,13 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
final val setCosts = Array(1.0 / 64, 1.0 / 128, 1.0 / 256)
final val copyCosts = Array(1.0 / 16, 1.0 / 32, 1.0 / 64)
final val fillCosts = Array(1.0 / 32, 1.0 / 64, 1.0 / 128)
final val bitbltCosts = Array(32, 16, 8)
final val totalVRAM: Int = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min Settings.get.vramSizes.length)
// These are dirty page bitblt budget costs
// a single bitblt can send a screen of data, which is n*set calls where set is writing an entire line
// So for each tier, we multiple the set cost with the number of lines the screen may have
final val bitbltCost: Double = Settings.get.bitbltCost * scala.math.pow(2, tier)
final val totalVRAM: Double = (maxResolution._1 * maxResolution._2) * Settings.get.vramSizes(0 max tier min 2)
var budgetExhausted: Boolean = false // for especially expensive calls, bitblt
// ----------------------------------------------------------------------- //
@ -96,12 +99,12 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
// ----------------------------------------------------------------------- //
private def consumeViewportPower(buffer: api.internal.TextBuffer, context: Context, budgetCost: Double, units: Int, factor: Double): Boolean = {
buffer match {
case _: component.GpuTextBuffer => true
case _ =>
private def resolveInvokeCosts(idx: Int, context: Context, budgetCost: Double, units: Int, factor: Double): Boolean = {
idx match {
case RESERVED_SCREEN_INDEX =>
context.consumeCallBudget(budgetCost)
consumePower(units, factor)
case _ => true
}
}
@ -140,38 +143,40 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
}
else if (size > (totalVRAM - calculateUsedMemory)) {
result(Unit, "not enough video memory")
} else if (node == null) {
result(Unit, "graphics card appears disconnected")
} else {
val format: PackedColor.ColorFormat = PackedColor.Depth.format(Settings.screenDepthsByTier(tier))
val buffer = new li.cil.oc.util.TextBuffer(width, height, format)
val page = component.GpuTextBuffer.wrap(nextAvailableBufferIndex, buffer)
val page = component.GpuTextBuffer.wrap(node.address, nextAvailableBufferIndex, buffer)
addBuffer(page)
result(page.id)
}
}
// this event occurs when the gpu is told a page was removed - we need to notify the screen of this
// we do this because the VideoRamAware trait only notifies itself, it doesn't assume there is a screen
override def onBufferRamDestroy(ids: Array[Int]): Unit = {
// we do this because the VideoRamDevice trait only notifies itself, it doesn't assume there is a screen
override def onBufferRamDestroy(id: Int): Unit = {
// first protect our buffer index - it needs to fall back to the screen if its buffer was removed
if (ids.contains(bufferIndex)) {
bufferIndex = RESERVED_SCREEN_INDEX
}
if (ids.nonEmpty) {
if (id != RESERVED_SCREEN_INDEX) {
screen(RESERVED_SCREEN_INDEX, s => s match {
case oc: component.traits.VideoRamAware => result(oc.removeBuffers(ids))
case oc: component.traits.VideoRamRasterizer => result(oc.removeBuffer(node.address, id))
case _ => result(true)// addon mod screen type that is not video ram aware
})
} else result(true)
}
if (id == bufferIndex) {
bufferIndex = RESERVED_SCREEN_INDEX
}
}
@Callback(direct = true, doc = """function(index: number): boolean -- Closes buffer at `index`. Returns true if a buffer closed. If the current buffer is closed, index moves to 0""")
def freeBuffer(context: Context, args: Arguments): Array[AnyRef] = {
val index: Int = args.optInteger(0, bufferIndex)
if (removeBuffers(Array(index))) result(true)
if (removeBuffers(Array(index)) == 1) result(true)
else result(Unit, "no buffer at index")
}
@Callback(direct = true, doc = """function(): number -- Closes all buffers and returns true on success. If the active buffer is closed, index moves to 0""")
@Callback(direct = true, doc = """function(): number -- Closes all buffers and returns the count. If the active buffer is closed, index moves to 0""")
def freeAllBuffers(context: Context, args: Arguments): Array[AnyRef] = result(removeAllBuffers())
@Callback(direct = true, doc = """function(): number -- returns the total memory size of the gpu vram. This does not include the screen.""")
@ -194,13 +199,14 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
// large dirty buffers need throttling so their budget cost is more
// clean buffers have no budget cost.
src match {
case page: GpuTextBuffer if page.dirty => dst match {
case page: GpuTextBuffer => dst match {
case _: GpuTextBuffer => 0.0 // no cost to write to ram
case _ => // screen target will need the new buffer
case _ if page.dirty => // screen target will need the new buffer
// small buffers are cheap, so increase with size of buffer source
bitbltCosts(tier) * (src.getWidth * src.getHeight) / (maxResolution._1 * maxResolution._2)
bitbltCost * (src.getWidth * src.getHeight) / (maxResolution._1 * maxResolution._2)
case _ => .001 // bitblt a clean page to screen has a minimal cost
}
case _ => 0.0 // from screen or from clean buffer is free
case _ => 0.0 // from screen is free
}
}
@ -209,7 +215,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
// rasterizing to the screen has the same cost as copy (in fact, screen-to-screen blt _is_ a copy
dst match {
case _: GpuTextBuffer => 0
case _ => Settings.get.gpuCopyCost / (maxResolution._1 * maxResolution._2)
case _ => Settings.get.gpuCopyCost / 15
}
}
@ -226,10 +232,27 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
val fromCol = args.optInteger(6, 1)
val fromRow = args.optInteger(7, 1)
val budgetCost: Double = determineBitbltBudgetCost(dst, src)
var budgetCost: Double = determineBitbltBudgetCost(dst, src)
val energyCost: Double = determineBitbltEnergyCost(dst)
val tierCredit: Double = ((tier + 1) * .5)
val overBudget: Double = budgetCost - tierCredit
if (consumeViewportPower(dst, context, budgetCost, w * h, energyCost)) {
if (overBudget > 0) {
if (budgetExhausted) { // we've thrown once before
if (overBudget > tierCredit) { // we need even more pause than just a single tierCredit
val pauseNeeded = overBudget - tierCredit
val seconds: Double = (pauseNeeded / tierCredit) / 20
context.pause(seconds)
}
budgetCost = 0 // remove the rest of the budget cost at this point
} else {
budgetExhausted = true
throw new LimitReachedException()
}
}
budgetExhausted = false
if (resolveInvokeCosts(dstIdx, context, budgetCost, w * h, energyCost)) {
if (dstIdx == srcIdx) {
val tx = col - fromCol
val ty = row - fromRow
@ -264,7 +287,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
s.setForegroundColor(0xFFFFFF)
s.setBackgroundColor(0x000000)
s match {
case oc: component.traits.VideoRamAware => oc.removeAllBuffers()
case oc: component.traits.VideoRamRasterizer => oc.removeAllBuffers()
case _ =>
}
}
@ -276,13 +299,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
}
@Callback(direct = true, doc = """function():string -- Get the address of the screen the GPU is currently bound to.""")
def getScreen(context: Context, args: Arguments): Array[AnyRef] = {
if (bufferIndex == RESERVED_SCREEN_INDEX) {
screen(s => result(s.node.address))
} else {
result(Unit, "the current text buffer is video ram")
}
}
def getScreen(context: Context, args: Arguments): Array[AnyRef] = screen(RESERVED_SCREEN_INDEX, s => result(s.node.address))
@Callback(direct = true, doc = """function():number, boolean -- Get the current background color and whether it's from the palette or not.""")
def getBackground(context: Context, args: Arguments): Array[AnyRef] =
@ -290,8 +307,10 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
@Callback(direct = true, doc = """function(value:number[, palette:boolean]):number, number or nil -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.""")
def setBackground(context: Context, args: Arguments): Array[AnyRef] = {
context.consumeCallBudget(setBackgroundCosts(tier))
val color = args.checkInteger(0)
if (bufferIndex == RESERVED_SCREEN_INDEX) {
context.consumeCallBudget(setBackgroundCosts(tier))
}
screen(s => {
val oldValue = s.getBackgroundColor
val (oldColor, oldIndex) =
@ -312,8 +331,10 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
@Callback(direct = true, doc = """function(value:number[, palette:boolean]):number, number or nil -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.""")
def setForeground(context: Context, args: Arguments): Array[AnyRef] = {
context.consumeCallBudget(setForegroundCosts(tier))
val color = args.checkInteger(0)
if (bufferIndex == RESERVED_SCREEN_INDEX) {
context.consumeCallBudget(setForegroundCosts(tier))
}
screen(s => {
val oldValue = s.getForegroundColor
val (oldColor, oldIndex) =
@ -338,10 +359,12 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
@Callback(direct = true, doc = """function(index:number, color:number):number -- Set the palette color at the specified palette index. Returns the previous value.""")
def setPaletteColor(context: Context, args: Arguments): Array[AnyRef] = {
context.consumeCallBudget(setPaletteColorCosts(tier))
val index = args.checkInteger(0)
val color = args.checkInteger(1)
context.pause(0.1)
if (bufferIndex == RESERVED_SCREEN_INDEX) {
context.consumeCallBudget(setPaletteColorCosts(tier))
context.pause(0.1)
}
screen(s => try {
val oldColor = s.getPaletteColor(index)
s.setPaletteColor(index, color)
@ -422,6 +445,16 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
@Callback(direct = true, doc = """function(x:number, y:number):string, number, number, number or nil, number or nil -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.""")
def get(context: Context, args: Arguments): Array[AnyRef] = {
// maybe one day:
// if (bufferIndex != RESERVED_SCREEN_INDEX && args.count() == 0) {
// return screen {
// case ram: GpuTextBuffer => {
// val nbt = new NBTTagCompound
// ram.data.save(nbt)
// result(nbt)
// }
// }
// }
val x = args.checkInteger(0) - 1
val y = args.checkInteger(1) - 1
screen(s => {
@ -455,11 +488,10 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
val vertical = args.optBoolean(3, false)
screen(s => {
if (consumeViewportPower(s, context, setCosts(tier), value.length, Settings.get.gpuSetCost)) {
if (resolveInvokeCosts(bufferIndex, context, setCosts(tier), value.length, Settings.get.gpuSetCost)) {
s.set(x, y, value, vertical)
result(true)
}
else result(Unit, "not enough energy")
} else result(Unit, "not enough energy")
})
}
@ -472,7 +504,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
val tx = args.checkInteger(4)
val ty = args.checkInteger(5)
screen(s => {
if (consumeViewportPower(s, context, copyCosts(tier), w * h, Settings.get.gpuCopyCost)) {
if (resolveInvokeCosts(bufferIndex, context, copyCosts(tier), w * h, Settings.get.gpuCopyCost)) {
s.copy(x, y, w, h, tx, ty)
result(true)
}
@ -490,7 +522,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
if (value.length == 1) screen(s => {
val c = value.charAt(0)
val cost = if (c == ' ') Settings.get.gpuClearCost else Settings.get.gpuFillCost
if (consumeViewportPower(s, context, fillCosts(tier), w * h, cost)) {
if (resolveInvokeCosts(bufferIndex, context, fillCosts(tier), w * h, cost)) {
s.fill(x, y, w, h, value.charAt(0))
result(true)
}
@ -608,7 +640,7 @@ class GraphicsCard(val tier: Int) extends AbstractManagedEnvironment with Device
val nbtPage = nbtPages.getCompoundTagAt(i)
val idx: Int = nbtPage.getInteger(NBT_PAGE_IDX)
val data = nbtPage.getCompoundTag(NBT_PAGE_DATA)
loadBuffer(idx, data)
loadBuffer(node.address, idx, data)
}
}
}

View File

@ -102,27 +102,32 @@ class Trade(val info: TradeInfo) extends AbstractValue {
def completeTrade(inventory: IInventory, recipe: MerchantRecipe, exact: Boolean) : Boolean = {
// Now we'll check if we have enough items to perform the trade, caching first
val firstInputStack = recipe.getItemToBuy
val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None
info.merchant.get match {
case Some(merchant) => {
val firstInputStack = recipe.getItemToBuy
val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None
def containsAccumulativeItemStack(stack: ItemStack) =
InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true, exact = exact).getCount == 0
def containsAccumulativeItemStack(stack: ItemStack) =
InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true, exact = exact).getCount == 0
// Check if we have enough to perform the trade.
if (!containsAccumulativeItemStack(firstInputStack) || !secondInputStack.forall(containsAccumulativeItemStack))
return false
// Check if we have enough to perform the trade.
if (!containsAccumulativeItemStack(firstInputStack) || !secondInputStack.forall(containsAccumulativeItemStack))
return false
// Now we need to check if we have enough inventory space to accept the item we get for the trade.
val outputStack = recipe.getItemToSell.copy()
// Now we need to check if we have enough inventory space to accept the item we get for the trade.
val outputStack = recipe.getItemToSell.copy()
// We established that out inventory allows to perform the trade, now actually do the trade.
InventoryUtils.extractFromInventory(firstInputStack, InventoryUtils.asItemHandler(inventory), exact = exact)
secondInputStack.map(InventoryUtils.extractFromInventory(_, InventoryUtils.asItemHandler(inventory), exact = exact))
InventoryUtils.insertIntoInventory(outputStack, InventoryUtils.asItemHandler(inventory), outputStack.getCount)
// We established that out inventory allows to perform the trade, now actually do the trade.
InventoryUtils.extractFromInventory(firstInputStack, InventoryUtils.asItemHandler(inventory), exact = exact)
secondInputStack.map(InventoryUtils.extractFromInventory(_, InventoryUtils.asItemHandler(inventory), exact = exact))
InventoryUtils.insertIntoInventory(outputStack, InventoryUtils.asItemHandler(inventory), outputStack.getCount)
// Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes.
info.merchant.get.orNull.useRecipe(recipe)
true
// Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes.
merchant.useRecipe(recipe)
true
}
case _ => false
}
}
}

View File

@ -282,7 +282,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac
override def consumeCallBudget(callCost: Double): Unit = {
if (architecture.isInitialized && !inSynchronizedCall) {
val clampedCost = math.max(0.001, callCost)
val clampedCost = math.max(0.0, callCost)
if (clampedCost > callBudget) {
throw new LimitReachedException()
}

View File

@ -0,0 +1,49 @@
package li.cil.oc.util
import net.minecraft.nbt.NBTTagCompound
object NbtDataStream {
def getShortArray(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = {
if (!nbt.hasKey(key)) {
return false
}
val rawByteReader = new java.io.ByteArrayInputStream(nbt.getByteArray(key))
val memReader = new java.io.DataInputStream(rawByteReader)
for (y <- 0 until h) {
for (x <- 0 until w) {
if (2 > memReader.available()) {
return true // not great, but get out now
}
array2d(y)(x) = memReader.readShort()
}
}
true
}
def getIntArrayLegacy(nbt: NBTTagCompound, key: String, array2d: Array[Array[Short]], w: Int, h: Int) : Boolean = {
if (!nbt.hasKey(key)) {
return false
}
// legacy format
val c = nbt.getIntArray(key)
for (y <- 0 until h) {
val rowColor = array2d(y)
for (x <- 0 until w) {
val index = x + y * w
if (index >= c.length) {
return true // not great, but, the read at least started
}
rowColor(x) = c(index).toShort
}
}
true
}
def setShortArray(nbt: NBTTagCompound, key: String, array: Array[Short]): Unit = {
val rawByteWriter = new java.io.ByteArrayOutputStream()
val memWriter = new java.io.DataOutputStream(rawByteWriter)
array.foreach(memWriter.writeShort(_))
nbt.setByteArray(key, rawByteWriter.toByteArray)
}
}

View File

@ -215,7 +215,14 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col
val dstColorLine = color(row_index + yOffset)
for (xOffset <- 0 until w) {
val srcChar = src.buffer(fromRow + yOffset - 1)(fromCol + xOffset - 1)
val srcColor = src.color(fromRow + yOffset - 1)(fromCol + xOffset - 1)
var srcColor = src.color(fromRow + yOffset - 1)(fromCol + xOffset - 1)
if (this.format.depth != src.format.depth) {
val fg = PackedColor.Color(PackedColor.unpackForeground(srcColor, src.format))
val bg = PackedColor.Color(PackedColor.unpackBackground(srcColor, src.format))
srcColor = PackedColor.pack(fg, bg, format)
}
if (srcChar != dstCharLine(col_index + xOffset) || srcColor != dstColorLine(col_index + xOffset)) {
changed = true
dstCharLine(col_index + xOffset) = srcChar
@ -262,15 +269,8 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col
foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette"))
background = PackedColor.Color(nbt.getInteger("background"), nbt.getBoolean("backgroundIsPalette"))
val c = nbt.getIntArray("color")
for (i <- 0 until h) {
val rowColor = color(i)
for (j <- 0 until w) {
val index = j + i * w
if (index < c.length) {
rowColor(j) = c(index).toShort
}
}
if (!NbtDataStream.getShortArray(nbt, "colors", color, w, h)) {
NbtDataStream.getIntArrayLegacy(nbt, "color", color, w, h)
}
}
@ -291,10 +291,10 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col
nbt.setInteger("background", _background.value)
nbt.setBoolean("backgroundIsPalette", _background.isPalette)
nbt.setTag("color", new NBTTagIntArray(color.flatten.map(_.toInt)))
NbtDataStream.setShortArray(nbt, "colors", color.flatten.map(_.toShort))
}
override def toString = {
override def toString: String = {
val b = StringBuilder.newBuilder
if (buffer.length > 0) {
b.appendAll(buffer(0))