mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-09 15:25:56 -04:00
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:
commit
012f55a7fa
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
{label = "OpenOS", reboot=true, setlabel=true, setboot=true}
|
||||
{label = "OpenOS", reboot=true, setlabel=true, setboot=true, noclobber={"etc/rc.cfg","home/.shrc"}}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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]]) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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]]) {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
49
src/main/scala/li/cil/oc/util/NbtDataStream.scala
Normal file
49
src/main/scala/li/cil/oc/util/NbtDataStream.scala
Normal 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)
|
||||
}
|
||||
}
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user