builder improvements

This commit is contained in:
kepler155c@gmail.com 2017-06-23 02:04:56 -04:00
parent 3f66a9397c
commit 7f99c0c69a
20 changed files with 2827 additions and 675 deletions

View File

@ -1,5 +1,39 @@
local class = require('class')
local TableDB = require('tableDB')
local JSON = require('json')
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/pymclevel/minecraft.yaml
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/Items/minecraft/blocks.json
local nameDB = TableDB({
fileName = 'blocknames.db'
})
function nameDB:load(dir, blockDB)
self.fileName = fs.combine(dir, self.fileName)
if fs.exists(self.fileName) then
TableDB.load(self)
end
self.blockDB = blockDB
end
function nameDB:getName(id, dmg)
return self:lookupName(id, dmg) or id .. ':' .. dmg
end
function nameDB:lookupName(id, dmg)
-- is it in the name db ?
local name = self:get({ id, dmg })
if name then
return name
end
-- is it in the block db ?
for _,v in pairs(self.blockDB.data) do
if v.strId == id and v.dmg == dmg then
return v.name
end
end
end
local blockDB = TableDB({
fileName = 'block.db',
@ -16,7 +50,7 @@ local blockDB = TableDB({
}
})
function blockDB:load(dir, sbDB, btDB)
function blockDB:load(dir)
self.fileName = fs.combine(dir, self.fileName)
if fs.exists(self.fileName) then
TableDB.load(self)
@ -26,90 +60,26 @@ function blockDB:load(dir, sbDB, btDB)
end
function blockDB:seedDB(dir)
-- http://lua-users.org/wiki/LuaCsv
function ParseCSVLine (line,sep)
local res = {}
local pos = 1
sep = sep or ','
while true do
local c = string.sub(line,pos,pos)
if (c == "") then break end
if (c == '"') then
-- quoted value (ignore separator within)
local txt = ""
repeat
local startp,endp = string.find(line,'^%b""',pos)
txt = txt..string.sub(line,startp+1,endp-1)
pos = endp + 1
c = string.sub(line,pos,pos)
if (c == '"') then txt = txt..'"' end
-- check first char AFTER quoted string, if it is another
-- quoted string without separator, then append it
-- this is the way to "escape" the quote char in a quote. example:
-- value1,"blub""blip""boing",value3 will result in blub"blip"boing for the middle
until (c ~= '"')
table.insert(res,txt)
assert(c == sep or c == "")
pos = pos + 1
else
-- no quotes used, just look for the first separator
local startp,endp = string.find(line,sep,pos)
if (startp) then
table.insert(res,string.sub(line,pos,startp-1))
pos = endp + 1
else
-- no separator found -> use rest of string and terminate
table.insert(res,string.sub(line,pos))
break
end
local blocks = JSON.decodeFromFile(fs.combine('sys/etc', 'blocks.json'))
if not blocks then
error('Unable to read blocks.json')
end
for strId, block in pairs(blocks) do
strId = 'minecraft:' .. strId
if type(block.name) == 'string' then
self:add(block.id, 0, block.name, strId)
else
for nid,name in pairs(block.name) do
self:add(block.id, nid - 1, name, strId)
end
end
return res
end
local f = fs.open(fs.combine('sys/etc', 'blockIds.csv'), "r")
if not f then
error('unable to read blockIds.csv')
end
local lastID = nil
while true do
local data = f.readLine()
if not data then
break
end
local line = ParseCSVLine(data, ',')
local strId = line[3] -- string ID
local nid -- schematic ID
local id = line[3]
local dmg = 0
local name = line[1]
if not strId or #strId == 0 then
strId = lastID
end
lastID = strId
local t = { }
string.gsub(line[2], '(%d+)', function(d) table.insert(t, d) end)
nid = tonumber(t[1])
dmg = 0
if t[2] then
dmg = tonumber(t[2])
end
self:add(nid, dmg, name, strId)
end
f.close()
self.dirty = true
self:flush()
-- self:flush()
end
function blockDB:lookup(id, dmg)
@ -117,27 +87,12 @@ function blockDB:lookup(id, dmg)
if not id then
return
end
if not id or not dmg then error('blockDB:lookup: nil passed', 2) end
local key = id .. ':' .. dmg
return self.data[key]
end
function blockDB:getName(id, dmg)
return self:lookupName(id, dmg) or id .. ':' .. dmg
end
function blockDB:lookupName(id, dmg)
if not id or not dmg then error('blockDB:lookupName: nil passed', 2) end
for _,v in pairs(self.data) do
if v.strId == id and v.dmg == dmg then
return v.name
end
end
end
function blockDB:add(id, dmg, name, strId)
local key = id .. ':' .. dmg
@ -170,8 +125,8 @@ function placementDB:load(dir, sbDB, btDB)
end
-- testing
-- self.dirty = true
-- self:flush()
self.dirty = true
--self:flush()
end
function placementDB:addSubsForBlockType(id, dmg, bt)
@ -192,11 +147,12 @@ function placementDB:addSubsForBlockType(id, dmg, bt)
odmg,
sub.sid or strId,
sub.sdmg or dmg,
sub.dir)
sub.dir,
sub.extra)
end
end
function placementDB:add(id, dmg, sid, sdmg, direction)
function placementDB:add(id, dmg, sid, sdmg, direction, extra)
if not id or not dmg then error('placementDB:add: nil passed', 2) end
local key = id .. ':' .. dmg
@ -212,6 +168,7 @@ function placementDB:add(id, dmg, sid, sdmg, direction)
sid = sid, -- string ID
sdmg = sdmg, -- dmg without placement info
direction = direction,
extra = extra,
}
end
@ -286,7 +243,7 @@ function standardBlockDB:seedDB()
[ '65:0' ] = 'wallsign-ladder',
[ '66:0' ] = 'rail',
[ '67:0' ] = 'stairs',
[ '68:0' ] = 'wallsign',
[ '68:0' ] = 'wallsign-ladder',
[ '69:0' ] = 'lever',
[ '71:0' ] = 'door',
[ '75:0' ] = 'torch',
@ -295,6 +252,7 @@ function standardBlockDB:seedDB()
[ '78:0' ] = 'flatten',
[ '81:0' ] = 'flatten',
[ '83:0' ] = 'flatten',
[ '84:0' ] = 'flatten', -- jukebox
[ '86:0' ] = 'pumpkin',
[ '90:0' ] = 'air',
[ '91:0' ] = 'pumpkin',
@ -312,6 +270,7 @@ function standardBlockDB:seedDB()
[ '115:0' ] = 'flatten',
[ '117:0' ] = 'flatten',
[ '118:0' ] = 'cauldron',
[ '120:0' ] = 'flatten', -- end portal
[ '126:0' ] = 'slab',
[ '126:1' ] = 'slab',
[ '126:2' ] = 'slab',
@ -339,7 +298,7 @@ function standardBlockDB:seedDB()
[ '155:2' ] = 'quartz-pillar',
[ '156:0' ] = 'stairs',
[ '157:0' ] = 'adp-rail',
[ '158:0' ] = 'hopper',
[ '158:0' ] = 'dispenser',
[ '161:0' ] = 'leaves',
[ '161:1' ] = 'leaves',
[ '162:0' ] = 'wood',
@ -347,15 +306,15 @@ function standardBlockDB:seedDB()
[ '163:0' ] = 'stairs',
[ '164:0' ] = 'stairs',
[ '167:0' ] = 'trapdoor',
[ '170:0' ] = 'quartz-pillar', -- hay bale
[ '170:0' ] = 'hay-bale', -- hay bale
[ '175:0' ] = 'largeplant',
[ '175:1' ] = 'largeplant',
[ '175:2' ] = 'largeplant', -- double tallgrass - an alternative would be to use grass as the bottom part, bonemeal as top part
[ '175:3' ] = 'largeplant',
[ '175:4' ] = 'largeplant',
[ '175:5' ] = 'largeplant',
[ '176:0' ] = 'banner',
[ '177:0' ] = 'wall_banner',
[ '176:0' ] = 'signpost',
[ '177:0' ] = 'wallsign-ladder',
[ '178:0' ] = 'truncate',
[ '180:0' ] = 'stairs',
[ '182:0' ] = 'slab',
@ -369,7 +328,7 @@ function standardBlockDB:seedDB()
[ '195:0' ] = 'door',
[ '196:0' ] = 'door',
[ '197:0' ] = 'door',
[ '198:0' ] = 'flatten',
[ '198:0' ] = 'end_rod', -- end rod
[ '205:0' ] = 'slab',
[ '210:0' ] = 'flatten',
[ '355:0' ] = 'bed',
@ -377,9 +336,9 @@ function standardBlockDB:seedDB()
[ '404:0' ] = 'comparator',
}
self.dirty = true
self:flush()
-- self:flush()
end
--[[-- BlockTypeDB --]]--
local blockTypeDB = TableDB({
fileName = 'blocktype.db',
@ -393,7 +352,7 @@ local blockTypeDB = TableDB({
}
}
})
function blockTypeDB:load(dir)
self.fileName = fs.combine(dir, self.fileName)
@ -403,7 +362,7 @@ function blockTypeDB:load(dir)
self:seedDB()
end
end
function blockTypeDB:addTemp(blockType, subs)
local bt = self.data[blockType]
if not bt then
@ -415,7 +374,8 @@ function blockTypeDB:addTemp(blockType, subs)
odmg = sub[1],
sid = sub[2],
sdmg = sub[3],
dir = sub[4]
dir = sub[4],
extra = sub[5]
})
end
self.dirty = true
@ -512,6 +472,11 @@ function blockTypeDB:seedDB()
{ 3, nil, 2, 'north-south-block' },
{ 4, nil, 2, 'east-west-block' }, -- should be east-west-block
})
blockTypeDB:addTemp('hay-bale', {
{ 0, nil, 0 },
{ 4, nil, 0, 'east-west-block' }, -- should be east-west-block
{ 8, nil, 0, 'north-south-block' },
})
blockTypeDB:addTemp('button', {
{ 1, nil, 0, 'west-block' },
{ 2, nil, 0, 'east-block' },
@ -526,14 +491,23 @@ function blockTypeDB:seedDB()
{ 3, nil, 0 },
})
blockTypeDB:addTemp('dispenser', {
{ 0, nil, 0 },
{ 1, nil, 0 },
{ 0, nil, 0, 'wrench-down' },
{ 1, nil, 0, 'wrench-up' },
{ 2, nil, 0, 'south' },
{ 3, nil, 0, 'north' },
{ 4, nil, 0, 'east' },
{ 5, nil, 0, 'west' },
{ 9, nil, 0 },
})
blockTypeDB:addTemp('end_rod', {
{ 0, nil, 0, 'wrench-down' },
{ 1, nil, 0, 'wrench-up' },
{ 2, nil, 0, 'south-block-flip' },
{ 3, nil, 0, 'north-block-flip' },
{ 4, nil, 0, 'east-block-flip' },
{ 5, nil, 0, 'west-block-flip' },
{ 9, nil, 0 },
})
blockTypeDB:addTemp('hopper', {
{ 0, nil, 0 },
{ 1, nil, 0 },
@ -541,6 +515,7 @@ function blockTypeDB:seedDB()
{ 3, nil, 0, 'north-block' },
{ 4, nil, 0, 'east-block' },
{ 5, nil, 0, 'west-block' },
{ 8, nil, 0 },
{ 9, nil, 0 },
{ 10, nil, 0 },
{ 11, nil, 0, 'south-block' },
@ -583,22 +558,22 @@ function blockTypeDB:seedDB()
{ 13, nil, 0, 'south' },
})
blockTypeDB:addTemp('signpost', {
{ 0, nil, 0, 'south' },
{ 1, nil, 0, 'south' },
{ 2, nil, 0, 'south' },
{ 3, nil, 0, 'south' },
{ 4, nil, 0, 'west' },
{ 5, nil, 0, 'west' },
{ 6, nil, 0, 'west' },
{ 7, nil, 0, 'west' },
{ 8, nil, 0, 'north' },
{ 9, nil, 0, 'north' },
{ 10, nil, 0, 'north' },
{ 11, nil, 0, 'north' },
{ 12, nil, 0, 'east' },
{ 13, nil, 0, 'east' },
{ 14, nil, 0, 'east' },
{ 15, nil, 0, 'east' },
{ 0, nil, 0, 'north' },
{ 1, nil, 0, 'north', { facing = 1 } },
{ 2, nil, 0, 'north', { facing = 2 } },
{ 3, nil, 0, 'north', { facing = 3 } },
{ 4, nil, 0, 'east' },
{ 5, nil, 0, 'east', { facing = 1 } },
{ 6, nil, 0, 'east', { facing = 2 } },
{ 7, nil, 0, 'east', { facing = 3 } },
{ 8, nil, 0, 'south' },
{ 9, nil, 0, 'south', { facing = 1 } },
{ 10, nil, 0, 'south', { facing = 2 } },
{ 11, nil, 0, 'south', { facing = 3 } },
{ 12, nil, 0, 'west' },
{ 13, nil, 0, 'west', { facing = 1 } },
{ 14, nil, 0, 'west', { facing = 2 } },
{ 15, nil, 0, 'west', { facing = 3 } },
})
blockTypeDB:addTemp('vine', {
{ 0, nil, 0 },
@ -684,18 +659,12 @@ function blockTypeDB:seedDB()
})
blockTypeDB:addTemp('wallsign-ladder', {
{ 0, nil, 0 },
{ 1, nil, 0 },
{ 2, nil, 0, 'south-block' },
{ 3, nil, 0, 'north-block' },
{ 4, nil, 0, 'east-block' },
{ 5, nil, 0, 'west-block' },
})
blockTypeDB:addTemp('wallsign', {
{ 0, nil, 0 },
{ 2, 'minecraft:sign', 0, 'south-block' },
{ 3, 'minecraft:sign', 0, 'north-block' },
{ 4, 'minecraft:sign', 0, 'east-block' },
{ 5, 'minecraft:sign', 0, 'west-block' },
})
blockTypeDB:addTemp('chest-furnace', {
{ 0, nil, 0 },
{ 2, nil, 0, 'south' },
@ -817,32 +786,6 @@ function blockTypeDB:seedDB()
{ 14,'minecraft:air', 0 },
{ 15,'minecraft:air', 0 },
})
blockTypeDB:addTemp('banner', {
{ 0, nil, 0, 'north' },
{ 1, nil, 0, 'east' },
{ 2, nil, 0, 'east' },
{ 3, nil, 0, 'east' },
{ 4, nil, 0, 'east' },
{ 5, nil, 0, 'east' },
{ 6, nil, 0, 'east' },
{ 7, nil, 0, 'east' },
{ 8, nil, 0, 'south' },
{ 9, nil, 0, 'west' },
{ 10, nil, 0, 'west' },
{ 11, nil, 0, 'west' },
{ 12, nil, 0, 'west' },
{ 13, nil, 0, 'west' },
{ 14, nil, 0, 'west' },
{ 15, nil, 0, 'west' },
})
blockTypeDB:addTemp('wall_banner', {
{ 0, nil, 0 },
{ 1, nil, 0 },
{ 2, nil, 0, 'south-block' },
{ 3, nil, 0, 'north-block' },
{ 4, nil, 0, 'east-block' },
{ 5, nil, 0, 'west-block' },
})
blockTypeDB:addTemp('cocoa', {
{ 0, nil, 0, 'south-block' },
{ 1, nil, 0, 'west-block' },
@ -858,7 +801,7 @@ function blockTypeDB:seedDB()
{ 11, nil, 0, 'east-block' },
})
self.dirty = true
self:flush()
-- self:flush()
end
local Blocks = class()
@ -866,10 +809,12 @@ function Blocks:init(args)
Util.merge(self, args)
self.blockDB = blockDB
self.nameDB = nameDB
blockDB:load(self.dir)
standardBlockDB:load(self.dir)
blockTypeDB:load(self.dir)
nameDB:load(self.dir, blockDB)
placementDB:load(self.dir, standardBlockDB, blockTypeDB)
end
@ -878,13 +823,18 @@ function Blocks:getRealBlock(id, dmg)
local p = placementDB:get({id, dmg})
if p then
return { id = p.sid, dmg = p.sdmg, direction = p.direction }
return { id = p.sid, dmg = p.sdmg, direction = p.direction, extra = p.extra }
end
local b = blockDB:get({id, dmg})
if b then
return { id = b.strId, dmg = b.dmg }
end
b = blockDB:get({id, 0})
if b then
return { id = b.strId, dmg = b.dmg }
end
return { id = id, dmg = dmg }
end

View File

@ -1,65 +1,124 @@
local class = require('class')
local TableDB = require('tableDB')
local Peripheral = require('peripheral')
local ChestProvider = class()
local keys = Util.transpose({
'damage',
'displayName',
'maxCount',
'maxDamage',
'name',
'nbtHash',
})
function ChestProvider:init(args)
args = args or { }
local defaults = {
items = { },
name = 'chest',
direction = 'up',
wrapSide = 'bottom',
}
Util.merge(self, defaults)
Util.merge(self, args)
local chest = Peripheral.getBySide(self.wrapSide)
if chest then
Util.merge(self, chest)
end
self.items = { } -- consolidated item info
self.cache = { }
self.name = 'chest'
self.direction = args.direction or 'up'
self.wrapSide = args.wrapSide or 'bottom'
self.p = peripheral.wrap(self.wrapSide)
if not self.itemInfoDB then
self.itemInfoDB = TableDB({
fileName = 'items.db'
})
self.itemInfoDB:load()
end
end
function ChestProvider:isValid()
return self.p and self.p.list
return not not self.list
end
function ChestProvider:refresh()
if self.p then
self.items = { }
for k,s in pairs(self.p.list()) do
function ChestProvider:getCachedItemDetails(item, k)
local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
local key = s.name .. ':' .. s.damage
local entry = self.items[key]
if not entry then
entry = self.cache[key]
if not entry then
local meta = self.p.getItemMeta(k) -- slow method.. cache for speed
if meta then
entry = {
id = s.name,
dmg = s.damage,
name = meta.displayName,
max_size = meta.maxCount,
}
self.cache[key] = entry
end
end
if entry then
entry = Util.shallowCopy(entry)
self.items[key] = entry
entry.qty = 0
end
local detail = self.itemInfoDB:get(key)
if not detail then
pcall(function() detail = self.getItemMeta(k) end)
if not detail then
return
end
if detail.name ~= item.name then
return
end
if detail.maxDamage and detail.maxDamage > 0 and detail.damage > 0 then
detail.displayName = detail.displayName .. ' (damaged)'
end
for _,k in ipairs(Util.keys(detail)) do
if not keys[k] then
detail[k] = nil
end
end
self.itemInfoDB:add(key, detail)
end
if detail then
return Util.shallowCopy(detail)
end
end
function ChestProvider:refresh(throttle)
return self:listItems(throttle)
end
-- provide a consolidated list of items
function ChestProvider:listItems(throttle)
self.cache = { }
local items = { }
throttle = throttle or Util.throttle()
for k,v in pairs(self.list()) do
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
local entry = self.cache[key]
if not entry then
entry = self:getCachedItemDetails(v, k)
if entry then
entry.qty = entry.qty + s.count
entry.dmg = entry.damage
entry.id = entry.name
entry.count = 0
entry.display_name = entry.displayName
entry.max_size = entry.maxCount
entry.nbt_hash = entry.nbtHash
entry.lname = entry.displayName:lower()
self.cache[key] = entry
table.insert(items, entry)
end
end
if entry then
entry.count = entry.count + v.count
entry.qty = entry.count
end
throttle()
end
return self.items
self.itemInfoDB:flush()
return items
end
function ChestProvider:getItemInfo(id, dmg)
for key,item in pairs(self.items) do
if item.id == id and item.dmg == dmg then
return item
end
function ChestProvider:getItemInfo(id, dmg, nbtHash)
if not self.cache then
self:listItems()
end
local key = table.concat({ id, dmg, nbtHash }, ':')
return self.cache[key]
end
function ChestProvider:craft(id, dmg, qty)
@ -69,31 +128,25 @@ function ChestProvider:craftItems(items)
end
function ChestProvider:provide(item, qty, slot)
if self.p then
local stacks = self.p.list()
for key,stack in pairs(stacks) do
if stack.name == item.id and stack.damage == item.dmg then
local amount = math.min(qty, stack.count)
self.p.pushItems(self.direction, key, amount, slot)
qty = qty - amount
if qty <= 0 then
break
end
local stacks = self.list()
for key,stack in pairs(stacks) do
if stack.name == item.id and stack.damage == item.dmg then
local amount = math.min(qty, stack.count)
self.pushItems(self.direction, key, amount, slot)
qty = qty - amount
if qty <= 0 then
break
end
end
end
end
function ChestProvider:extract(slot, qty)
if self.p then
self.p.pushItems(self.direction, slot, qty)
end
self.pushItems(self.direction, slot, qty)
end
function ChestProvider:insert(slot, qty)
if self.p then
self.p.pullItems(self.direction, slot, qty)
end
self.pullItems(self.direction, slot, qty)
end
return ChestProvider

View File

@ -139,17 +139,25 @@ function Event.addThread(fn)
end
function Event.pullEvents(...)
Process:addThread(_pullEvents)
local routines = { ... }
if #routines > 0 then
Process:addThread(_pullEvents)
for _, routine in ipairs(routines) do
Process:addThread(routine)
end
end
while true do
local e = Process:pullEvent()
if exitPullEvents or e == 'terminate' then
break
end
end
else
while true do
local e = Process:pullEvent()
if exitPullEvents or e == 'terminate' then
break
local e = { os.pullEvent() }
Event.processEvent(e)
if exitPullEvents or e == 'terminate' then
break
end
end
end
end

View File

@ -205,7 +205,7 @@ end
function json.decodeFromFile(path)
local file = assert(fs.open(path, "r"))
local decoded = decode(file.readAll())
local decoded = json.decode(file.readAll())
file.close()
return decoded
end

View File

@ -51,13 +51,15 @@ function Peripheral.addDevice(deviceList, side)
end
deviceList[name] = peripheral.wrap(side)
Util.merge(deviceList[name], {
name = name,
type = ptype,
side = side,
})
if deviceList[name] then
Util.merge(deviceList[name], {
name = name,
type = ptype,
side = side,
})
return deviceList[name]
return deviceList[name]
end
end
function Peripheral.getBySide(side)

View File

@ -62,13 +62,6 @@ function RefinedProvider:getCachedItemDetails(item)
end
detail.lname = detail.displayName:lower()
-- backwards capability
detail.dmg = detail.damage
detail.id = detail.name
detail.qty = detail.count
detail.display_name = detail.displayName
detail.nbtHash = item.nbtHash
local t = { }
for _,key in pairs(keys) do
t[key] = detail[key]
@ -76,8 +69,6 @@ function RefinedProvider:getCachedItemDetails(item)
detail = t
self.itemInfoDB:add(key, detail)
os.sleep(0) -- prevent timeout on large inventories
end
end
if detail then

View File

@ -148,20 +148,18 @@ function Schematic:parse(a, h, containsName, spinner)
end
-- end http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/
function Schematic:copyBlocks(iblocks, oblocks, spinner)
function Schematic:copyBlocks(iblocks, oblocks, throttle)
for k,b in ipairs(iblocks) do
oblocks[k] = Util.shallowCopy(b)
if spinner then
if (k % 1000) == 0 then
spinner:spin()
end
if (k % 1000) == 0 then
throttle()
end
end
end
function Schematic:reload()
function Schematic:reload(throttle)
self.blocks = { }
self:copyBlocks(self.originalBlocks, self.blocks)
self:copyBlocks(self.originalBlocks, self.blocks, throttle)
for _,ri in pairs(self.rowIndex) do
ri.loaded = false
@ -283,7 +281,7 @@ function Schematic:loadpass(fh, spinner)
self:assignDamages(spinner)
self.damages = nil
self:copyBlocks(self.blocks, self.originalBlocks, spinner)
self:copyBlocks(self.blocks, self.originalBlocks, function() spinner:spin() end)
spinner:stop()
end
@ -488,6 +486,37 @@ function Schematic:bestSide(b, chains, ...)
})
end
function Schematic:bestFlipSide(b, chains)
-- If there is a block to place this one against
local directions = {
[ 'east-block-flip' ] = 'east',
[ 'west-block-flip' ] = 'west',
[ 'north-block-flip' ] = 'north',
[ 'south-block-flip' ] = 'south',
}
local d = directions[b.direction]
local hi = turtle.getHeadingInfo(d)
local _, fb = self:findIndexAt(b.x + hi.xd, b.z + hi.zd, b.y)
if fb then
self:addPlacementChain(chains, {
{ x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against
{ x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc)
{ x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle
})
b.direction = d .. '-block'
else
self:addPlacementChain(chains, {
{ x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc)
{ x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle
{ x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against
})
b.direction = turtle.getHeadingInfo((hi.heading + 2) % 4).direction .. '-block'
end
end
function Schematic:bestOfTwoSides(b, chains, side1, side2) -- could be better
local sb
@ -617,12 +646,23 @@ function Schematic:determineBlockPlacement(y)
[ 'west-block-vine' ] = 'west-block',
[ 'north-block-vine' ] = 'north-block'
}
local flipDirections = {
[ 'east-block-flip' ] = 'east-block',
[ 'south-block-flip' ] = 'south-block',
[ 'west-block-flip' ] = 'west-block',
[ 'north-block-flip' ] = 'north-block'
}
local dirtyBlocks = {}
local dirtyBlocks2 = {}
local chains = {}
local ri = self.rowIndex[y]
if not ri then
ri = { s = -1, e = -2 }
self.rowIndex[y] = ri
end
for k = ri.s, ri.e do
local b = self.blocks[k]
local d = b.direction
@ -757,6 +797,8 @@ function Schematic:determineBlockPlacement(y)
b.heading = (turtle.getHeadingInfo(sd[1]).heading + 2) % 4
end
end
elseif flipDirections[d] then
self:bestFlipSide(b, chains)
end
if blockDirections[d] then
@ -790,6 +832,7 @@ function Schematic:determineBlockPlacement(y)
self:setPlacementOrder(spinner, chains)
local plane = self:optimizeRoute(spinner, y)
term.clearLine()
spinner:stop()
for k,b in ipairs(plane) do

View File

@ -331,7 +331,7 @@ function Manager:click(button, x, y)
if button == 1 then
local c = os.clock()
if self.doubleClickTimer and (c - self.doubleClickTimer < 1) and
if self.doubleClickTimer and (c - self.doubleClickTimer < 1.5) and
self.doubleClickX == x and self.doubleClickY == y and
self.doubleClickElement == clickEvent.element then
button = 3
@ -1076,8 +1076,8 @@ end
UI.TransitionSlideLeft = class()
UI.TransitionSlideLeft.defaults = {
UIElement = 'TransitionSlideLeft',
ticks = 12,
easing = 'outBounce',
ticks = 4,
easing = 'outQuint',
}
function UI.TransitionSlideLeft:init(args)
local defaults = UI:getDefaults(UI.TransitionSlideLeft, args)
@ -1116,11 +1116,11 @@ end
UI.TransitionSlideRight = class()
UI.TransitionSlideRight.defaults = {
UIElement = 'TransitionSlideRight',
ticks = 12,
easing = 'outBounce',
ticks = 4,
easing = 'outQuint',
}
function UI.TransitionSlideRight:init(args)
local defaults = UI:getDefaults(UI.TransitionSlideLeft, args)
local defaults = UI:getDefaults(UI.TransitionSlideRight, args)
UI.setProperties(self, defaults)
self.pos = { x = self.x }
@ -2520,6 +2520,72 @@ function UI.Notification:display(value, timeout)
end)
end
--[[-- Throttle --]]--
UI.Throttle = class(UI.Window)
UI.Throttle.defaults = {
UIElement = 'Throttle',
backgroundColor = colors.gray,
height = 6,
width = 10,
timeout = .095,
ctr = 0,
image = {
' //) (O )~@ &~&-( ?Q ',
' //) (O )- @ \-( ?) && ',
' //) (O ), @ \-(?) && ',
' //) (O ). @ \-d ) (@ '
}
}
function UI.Throttle:init(args)
local defaults = UI:getDefaults(UI.Throttle, args)
UI.Window.init(self, defaults)
end
function UI.Throttle:setParent()
self.x = math.ceil((self.parent.width - self.width) / 2)
self.y = math.ceil((self.parent.height - self.height) / 2)
UI.Window.setParent(self)
end
function UI.Throttle:enable()
self.enabled = false
end
function UI.Throttle:disable()
if self.canvas then
self.enabled = false
self.canvas:removeLayer()
self.canvas = nil
self.c = nil
end
end
function UI.Throttle:update()
local cc = os.clock()
if not self.c then
self.c = cc
elseif cc > self.c + self.timeout then
os.sleep(0)
self.c = os.clock()
self.enabled = true
if not self.canvas then
self.canvas = UI.term.canvas:addLayer(self, self.backgroundColor, colors.cyan)
self.canvas:setVisible(true)
self:clear(colors.cyan)
end
local image = self.image[self.ctr + 1]
local width = self.width - 2
for i = 0, #self.image do
self:write(2, i + 2, image:sub(width * i + 1, width * i + width), colors.black, colors.white)
end
self.ctr = (self.ctr + 1) % #self.image
self:sync()
end
end
--[[-- GridLayout --]]--
UI.GridLayout = class(UI.Window)
UI.GridLayout.defaults = {

View File

@ -27,8 +27,8 @@ function Util.throttle(fn)
return function(...)
local nts = os.clock()
if nts > ts + timeout then
ts = nts
os.sleep(0)
ts = os.clock()
if fn then
fn(...)
end

View File

@ -160,10 +160,14 @@ end
function page:runScript(scriptName)
if self.turtle then
self.notification:info('Connecting')
self:sync()
local cmd = string.format('Script %d %s', self.turtle.id, scriptName)
local ot = term.redirect(nullTerm)
pcall(function() shell.run(cmd) end)
term.redirect(ot)
self.notification:success('Sent')
end
end

File diff suppressed because it is too large Load Diff

View File

@ -189,10 +189,10 @@ itemPage = UI.Page {
backgroundColor = colors.green
},
displayName = UI.Window {
x = 5, y = 3, width = UI.term.width - 10, height = 3,
x = 5, y = 2, width = UI.term.width - 10, height = 3,
},
form = UI.Form {
x = 4, y = 6, height = 10, rex = -4,
x = 4, y = 4, height = 10, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,

350
sys/apps/shapes.lua Normal file
View File

@ -0,0 +1,350 @@
require = requireInjector(getfenv(1))
local UI = require('ui')
local GPS = require('gps')
local Socket = require('socket')
multishell.setTitle(multishell.getCurrent(), 'Shapes')
local args = { ... }
local turtleId = args[1] or error('Supply turtle ID')
turtleId = tonumber(turtleId)
local script = [[
require = requireInjector(getfenv(1))
local GPS = require('gps')
local ChestProvider = require('chestProvider18')
local Point = require('point')
local itemProvider
function dumpInventory()
for i = 1, 16 do
local qty = turtle.getItemCount(i)
if qty > 0 then
itemProvider:insert(i, qty)
end
if turtle.getItemCount(i) ~= 0 then
print('Provider is full or missing - make space or replace')
print('Press enter to continue')
read()
end
end
turtle.select(1)
end
local function refuel()
while turtle.getFuelLevel() < 4000 do
print('Refueling')
turtle.select(1)
itemProvider:provide({ id = 'minecraft:coal', dmg = 0 }, 64, 1)
if turtle.getItemCount(1) == 0 then
print('Out of fuel, add fuel to chest/ME system')
turtle.status = 'waiting'
os.sleep(5)
else
turtle.refuel(64)
end
end
end
local function goto(pt)
while not turtle.gotoPoint(pt) do
print('stuck')
os.sleep(5)
end
end
local function pathTo(pt)
while not turtle.pathfind(pt) do
print('stuck')
os.sleep(5)
end
end
local function resupply()
if data.suppliesPt then
pathTo(data.suppliesPt)
itemProvider = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
dumpInventory()
refuel()
end
end
local function makePlane(y)
local pt = { x = math.min(data.startPt.x, data.endPt.x),
ex = math.max(data.startPt.x, data.endPt.x),
z = math.min(data.startPt.z, data.endPt.z),
ez = math.max(data.startPt.z, data.endPt.z) }
local blocks = { }
for z = pt.z, pt.ez do
for x = pt.x, pt.ex do
table.insert(blocks, { x = x, y = y, z = z })
end
end
return blocks
end
local function optimizeRoute(plane, ptb)
local maxDistance = 99999999
local function getNearestNeighbor(p, pt, threshold)
local key, block, heading
local moves = maxDistance
local function getMoves(b, k)
local distance = math.abs(pt.x - b.x) + math.abs(pt.z - b.z)
if distance < moves then
-- this operation is expensive - only run if distance is close
local c, h = Point.calculateMoves(pt, b, distance)
if c < moves then
block = b
key = k
moves = c
heading = h
end
end
end
local function blockReady(b)
return not b.u
end
local mid = pt.index
local forward = mid + 1
local backward = mid - 1
while forward <= #p or backward > 0 do
if forward <= #p then
local b = p[forward]
if blockReady(b) then
getMoves(b, forward)
if moves <= threshold then
break
end
if moves < maxDistance and math.abs(b.z - pt.z) > moves and pt.index > 0 then
forward = #p
end
end
forward = forward + 1
end
if backward > 0 then
local b = p[backward]
if blockReady(b) then
getMoves(b, backward)
if moves <= threshold then
break
end
if moves < maxDistance and math.abs(pt.z - b.z) > moves then
backward = 0
end
end
backward = backward - 1
end
end
pt.x = block.x
pt.z = block.z
pt.y = block.y
pt.heading = heading
pt.index = key
block.u = true
return block
end
local throttle = Util.throttle()
local t = { }
ptb.index = 0
local threshold = 0
for i = 1, #plane do
local b = getNearestNeighbor(plane, ptb, threshold)
table.insert(t, b)
throttle()
threshold = 1
end
return t
end
local function clear()
local pt = Util.shallowCopy(data.startPt)
pt.y = math.min(data.startPt.y, data.endPt.y)
pt.heading = 0
local osy = pt.y
local sy = osy + 1
local ey = math.max(data.startPt.y, data.endPt.y)
local firstPlane = true
resupply()
while true do
if sy > ey then
sy = ey
end
local plane = makePlane(sy)
plane = optimizeRoute(plane, pt)
if firstPlane then
turtle.pathfind(plane[1])
turtle.setPolicy(turtle.policies.digAttack)
firstPlane = false
end
for _,b in ipairs(plane) do
turtle.gotoPoint(b)
if sy < ey then
turtle.digUp()
end
if sy > osy then
turtle.digDown()
end
if turtle.abort then
break
end
end
if turtle.abort then
break
end
if sy + 1 >= ey then
break
end
sy = sy + 3
end
turtle.setPolicy(turtle.policies.none)
resupply()
end
turtle.run(function()
turtle.status = 'Clearing'
if turtle.enableGPS() then
local pt = Util.shallowCopy(turtle.point)
local s, m = pcall(clear)
pathTo(pt)
if not s and m then
error(m)
read()
end
end
end)
]]
local data = Util.readTable('/usr/config/shapes') or { }
local page = UI.Page {
titleBar = UI.TitleBar { title = 'Shapes' },
info = UI.Window { x = 5, y = 3, height = 1 },
startCoord = UI.Button { x = 2, y = 6, text = 'Start ', event = 'startCoord' },
endCoord = UI.Button { x = 2, y = 8, text = 'End ', event = 'endCoord' },
supplies = UI.Button { x = 2, y = 10, text = 'Supplies', event = 'supplies' },
cancel = UI.Button { rx = 2, ry = -2, text = 'Abort', event = 'cancel' },
begin = UI.Button { rx = -7, ry = -2, text = 'Begin', event = 'begin' },
accelerators = { q = 'quit' },
notification = UI.Notification(),
statusBar = UI.StatusBar(),
}
function page.info:draw()
local function size(a, b)
return (math.abs(a.x - b.x) + 1) *
(math.abs(a.y - b.y) + 1) *
(math.abs(a.z - b.z) + 1)
end
self:clear()
if not data.startPt then
self:write(1, 1, 'Set starting corner')
elseif not data.endPt then
self:write(1, 1, 'Set ending corner')
else
self:write(1, 1, 'Blocks: ' .. size(data.startPt, data.endPt))
end
end
function page:getPoint()
local pt = GPS.getPoint()
if not pt then
self.notification:error('GPS not available')
end
return pt
end
function page:runFunction(id, script)
self.notification:info('Connecting')
local fn, msg = loadstring(script, 'script')
if not fn then
self.notification:error('Error in script')
debug(msg)
return
end
local socket = Socket.connect(id, 161)
if not socket then
self.notification:error('Unable to connect')
return
end
socket:write({ type = 'script', args = script })
socket:close()
self.notification:success('Sent')
end
function page:eventHandler(event)
if event.type == 'startCoord' then
data.startPt = self:getPoint()
if data.startPt then
self.statusBar:setStatus('starting corner set')
Util.writeTable('/usr/config/shapes', data)
end
self:draw()
elseif event.type == 'endCoord' then
data.endPt = self:getPoint()
if data.endPt then
self.statusBar:setStatus('ending corner set')
Util.writeTable('/usr/config/shapes', data)
end
self:draw()
elseif event.type == 'supplies' then
data.suppliesPt = self:getPoint()
if data.suppliesPt then
self.statusBar:setStatus('supplies location set')
Util.writeTable('/usr/config/shapes', data)
end
elseif event.type == 'begin' then
if data.startPt and data.endPt then
local s = 'local data = ' .. textutils.serialize(data) .. script
self:runFunction(turtleId, s)
else
self.notification:error('Corners not set')
end
self.statusBar:setStatus('')
elseif event.type == 'cancel' then
self:runFunction(turtleId, 'turtle.abortAction()')
self.statusBar:setStatus('')
else
return UI.Page.eventHandler(self, event)
end
return true
end
UI:setPage(page)
UI:pullEvents()
UI.term:reset()

View File

@ -4,10 +4,14 @@ local Event = require('event')
local UI = require('ui')
local RefinedProvider = require('refinedProvider')
local MEProvider = require('meProvider')
local ChestProvider = require('chestProvider18')
local storage = RefinedProvider()
if not storage:isValid() then
storage = MEProvider()
if not storage:isValid() then
storage = ChestProvider()
end
end
if not storage:isValid() then
@ -106,7 +110,7 @@ local function uniqueKey(item)
end
function changedPage:refresh()
local t = storage:listItems('all')
local t = storage:listItems()
if not t or Util.empty(t) then
self:clear()

View File

@ -31,6 +31,7 @@ Logger.filter('modem_send', 'event', 'ui')
Logger.setWirelessLogging()
local __BUILDER_ID = 6
local itemInfoDB
local Builder = {
version = '1.70',
@ -116,7 +117,7 @@ function Builder:refuel()
self.itemProvider:provide(self.fuelItem, 64, 1)
if turtle.getItemCount(1) == 0 then
Builder:log('Out of fuel, add coal to chest/ME system')
turtle.setHeading(0)
--turtle.setHeading(0)
os.sleep(5)
else
turtle.refuel(64)
@ -160,6 +161,11 @@ function Builder:getSupplies()
if s.qty < s.need then
table.insert(t, s)
local name = s.name or s.id .. ':' .. s.dmg
local item = itemInfoDB:get({ s.id, s.dmg })
if item then
name = item.displayName
end
Builder:log('Need %d %s', s.need - s.qty, name)
end
end
@ -400,6 +406,12 @@ __BUILDER_ID = tonumber(args[1])
maxStackDB:load()
itemInfoDB = TableDB({
fileName = 'items.db'
})
itemInfoDB:load()
Builder.itemProvider = MEProvider({ direction = args[2] })
if not Builder.itemProvider:isValid() then
local sides = {

View File

@ -45,6 +45,7 @@ Leaves (Spruce),18:1,,Solid Block
Leaves (Birch),18:2,,Solid Block
Leaves (Jungle),18:3,,Solid Block
Sponge,19,minecraft:sponge,Solid Block
Wet Sponge,19:1,minecraft:sponge,Solid Block
Glass,20,minecraft:glass,Solid Block
Lapis Lazuli Ore,21,minecraft:lapis_ore,Solid Block
Lapis Lazuli Block,22,minecraft:lapis_block,Solid Block

1 Air 0 minecraft:air Non-Solid Block
45 Leaves (Birch) 18:2 Solid Block
46 Leaves (Jungle) 18:3 Solid Block
47 Sponge 19 minecraft:sponge Solid Block
48 Wet Sponge 19:1 minecraft:sponge Solid Block
49 Glass 20 minecraft:glass Solid Block
50 Lapis Lazuli Ore 21 minecraft:lapis_ore Solid Block
51 Lapis Lazuli Block 22 minecraft:lapis_block Solid Block

1775
sys/etc/blocks.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -72,10 +72,10 @@ function turtle.run(fn, ...)
local s, m = pcall(function() fn(unpack(args)) end)
turtle.abort = false
releaseTicket(ticketId)
os.queueEvent('turtle_response')
if not s and m then
printError(m)
end
os.queueEvent('turtle_response')
return s, m
end
end

View File

@ -30,6 +30,6 @@ end
function turtle.setGPSHome()
if turtle.enableGPS() then
turtle.storeLocation('gpsHome', turtle.point)
turtle.gotoPoint(tturtle.point)
turtle.gotoPoint(turtle.point)
end
end

View File

@ -19,6 +19,8 @@ Event.addHandler('peripheral', function(event, side)
term.setTextColor(attachColor)
Util.print('[%s] %s attached', dev.side, dev.name)
os.queueEvent('device_attach', dev.name)
else
Util.print('[%s] attached failed', side)
end
end
end)