cleanup
This commit is contained in:
@@ -42,7 +42,7 @@ local blockDB = TableDB()
|
||||
|
||||
function blockDB:load()
|
||||
|
||||
local blocks = JSON.decodeFromFile('usr/etc/blocks.json'))
|
||||
local blocks = JSON.decodeFromFile('usr/etc/blocks.json')
|
||||
|
||||
if not blocks then
|
||||
error('Unable to read blocks.json')
|
||||
|
||||
@@ -3,6 +3,20 @@ local TableDB = require('tableDB')
|
||||
|
||||
local itemDB = TableDB({ fileName = 'usr/config/items.db' })
|
||||
|
||||
local function splitKey(key, item)
|
||||
|
||||
item = item or { }
|
||||
|
||||
local t = Util.split(key, '(.-):')
|
||||
if #t[#t] > 8 then
|
||||
item.nbtHash = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
item.name = table.concat(t, ':')
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
function itemDB:get(key)
|
||||
|
||||
local item = TableDB.get(self, key)
|
||||
@@ -34,6 +48,54 @@ function itemDB:makeKey(item)
|
||||
return { item.name, item.damage, item.nbtHash }
|
||||
end
|
||||
|
||||
-- Accepts: "minecraft:stick:0" or { name = 'minecraft:stick', damage = 0 }
|
||||
function itemDB:getName(item)
|
||||
|
||||
if type(item) == 'string' then
|
||||
item = splitKey(item)
|
||||
end
|
||||
|
||||
local detail = self:get(self:makeKey(item))
|
||||
if detail then
|
||||
return detail.displayName
|
||||
end
|
||||
return item.name .. ':' .. item.damage
|
||||
end
|
||||
|
||||
function itemDB:load()
|
||||
|
||||
TableDB.load(self)
|
||||
|
||||
for key,item in pairs(self.data) do
|
||||
splitKey(key, item)
|
||||
item.maxDamage = item.maxDamage or 0
|
||||
item.maxCount = item.maxCount or 64
|
||||
end
|
||||
end
|
||||
|
||||
function itemDB:flush()
|
||||
if self.dirty then
|
||||
|
||||
local t = { }
|
||||
for k,v in pairs(self.data) do
|
||||
v = Util.shallowCopy(v)
|
||||
v.name = nil
|
||||
v.damage = nil
|
||||
v.nbtHash = nil
|
||||
if v.maxDamage == 0 then
|
||||
v.maxDamage = nil
|
||||
end
|
||||
if v.maxCount == 64 then
|
||||
v.maxCount = nil
|
||||
end
|
||||
t[k] = v
|
||||
end
|
||||
|
||||
Util.writeTable(self.fileName, t)
|
||||
self.dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
itemDB:load()
|
||||
|
||||
return itemDB
|
||||
|
||||
@@ -27,7 +27,7 @@ function RefinedAdapter:init(args)
|
||||
Util.merge(self, controller)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RefinedAdapter:isValid()
|
||||
return not not self.listAvailableItems
|
||||
end
|
||||
|
||||
@@ -7,17 +7,15 @@ function TableDB:init(args)
|
||||
fileName = '',
|
||||
dirty = false,
|
||||
data = { },
|
||||
tabledef = { },
|
||||
}
|
||||
Util.merge(defaults, args)
|
||||
Util.merge(self, defaults)
|
||||
end
|
||||
|
||||
function TableDB:load()
|
||||
local table = Util.readTable(self.fileName)
|
||||
if table then
|
||||
self.data = table.data
|
||||
self.tabledef = table.tabledef
|
||||
local t = Util.readTable(self.fileName)
|
||||
if t then
|
||||
self.data = t.data or t
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,10 +41,7 @@ end
|
||||
|
||||
function TableDB:flush()
|
||||
if self.dirty then
|
||||
Util.writeTable(self.fileName, {
|
||||
-- tabledef = self.tabledef,
|
||||
data = self.data,
|
||||
})
|
||||
Util.writeTable(self.fileName, self.data)
|
||||
self.dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,11 +3,11 @@ local Util = require('util')
|
||||
|
||||
local Craft = { }
|
||||
|
||||
local function clearGrid(chestAdapter)
|
||||
local function clearGrid(inventoryAdapter)
|
||||
for i = 1, 16 do
|
||||
local count = turtle.getItemCount(i)
|
||||
if count > 0 then
|
||||
chestAdapter:insert(i, count)
|
||||
inventoryAdapter:insert(i, count)
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
@@ -19,7 +19,7 @@ end
|
||||
local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 2 then
|
||||
if #t[#t] > 8 then
|
||||
item.nbtHash = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
@@ -39,13 +39,13 @@ local function getItemCount(items, key)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function turtleCraft(recipe, qty, chestAdapter)
|
||||
local function turtleCraft(recipe, qty, inventoryAdapter)
|
||||
|
||||
clearGrid(chestAdapter)
|
||||
clearGrid(inventoryAdapter)
|
||||
|
||||
for k,v in pairs(recipe.ingredients) do
|
||||
local item = splitKey(v)
|
||||
chestAdapter:provide(item, qty, k)
|
||||
inventoryAdapter:provide(item, qty, k)
|
||||
if turtle.getItemCount(k) == 0 then -- ~= qty then
|
||||
-- FIX: ingredients cannot be stacked
|
||||
return false
|
||||
@@ -55,9 +55,9 @@ local function turtleCraft(recipe, qty, chestAdapter)
|
||||
return turtle.craft()
|
||||
end
|
||||
|
||||
function Craft.craftRecipe(recipe, count, chestAdapter)
|
||||
function Craft.craftRecipe(recipe, count, inventoryAdapter)
|
||||
|
||||
local items = chestAdapter:listItems()
|
||||
local items = inventoryAdapter:listItems()
|
||||
|
||||
local function sumItems(items)
|
||||
-- produces { ['minecraft:planks:0'] = 8 }
|
||||
@@ -78,10 +78,10 @@ function Craft.craftRecipe(recipe, count, chestAdapter)
|
||||
if itemCount < icount * count then
|
||||
local irecipe = Craft.recipes[key]
|
||||
if irecipe then
|
||||
Util.print('Crafting %d %s', icount * count - itemCount, key)
|
||||
--Util.print('Crafting %d %s', icount * count - itemCount, key)
|
||||
if not Craft.craftRecipe(irecipe,
|
||||
icount * count - itemCount,
|
||||
chestAdapter) then
|
||||
inventoryAdapter) then
|
||||
turtle.select(1)
|
||||
return
|
||||
end
|
||||
@@ -89,7 +89,7 @@ Util.print('Crafting %d %s', icount * count - itemCount, key)
|
||||
end
|
||||
end
|
||||
repeat
|
||||
if not turtleCraft(recipe, math.min(count, maxCount), chestAdapter) then
|
||||
if not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then
|
||||
turtle.select(1)
|
||||
return false
|
||||
end
|
||||
@@ -101,7 +101,7 @@ Util.print('Crafting %d %s', icount * count - itemCount, key)
|
||||
end
|
||||
|
||||
-- given a certain quantity, return how many of those can be crafted
|
||||
function Craft.getCraftableAmount(recipe, count, items)
|
||||
function Craft.getCraftableAmount(recipe, count, items, missing)
|
||||
|
||||
local function sumItems(recipe, items, summedItems, count)
|
||||
|
||||
@@ -116,6 +116,9 @@ function Craft.getCraftableAmount(recipe, count, items)
|
||||
summedItem = summedItem + sumItems(irecipe, items, summedItems, 1)
|
||||
end
|
||||
if summedItem <= 0 then
|
||||
if missing then
|
||||
missing.name = item
|
||||
end
|
||||
return canCraft
|
||||
end
|
||||
summedItems[item] = summedItem - 1
|
||||
@@ -126,7 +129,7 @@ function Craft.getCraftableAmount(recipe, count, items)
|
||||
return canCraft
|
||||
end
|
||||
|
||||
return sumItems(recipe, items, { }, math.ceil(count / recipe.count))
|
||||
return sumItems(recipe, items, { }, math.ceil(count / recipe.count), missing)
|
||||
end
|
||||
|
||||
function Craft.canCraft(item, count, items)
|
||||
|
||||
@@ -2,8 +2,8 @@ local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local checkedNodes = { }
|
||||
local nodes = { }
|
||||
local box = { }
|
||||
local nodes = { }
|
||||
local box = { }
|
||||
local oldCallback
|
||||
|
||||
local function toKey(pt)
|
||||
@@ -149,7 +149,7 @@ return function(startPt, endPt, firstPt, verbose)
|
||||
print(string.format('%d nodes remaining', Util.size(nodes)))
|
||||
end
|
||||
|
||||
if Util.size(nodes) == 0 then
|
||||
if not next(nodes) then
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
@@ -140,18 +140,22 @@ function page:enable(turtle)
|
||||
end
|
||||
|
||||
function page:runFunction(script, nowrap)
|
||||
if not socket then
|
||||
socket = Socket.connect(self.turtle.id, 161)
|
||||
for i = 1, 2 do
|
||||
if not socket then
|
||||
self.notification:error('Unable to connect')
|
||||
return
|
||||
socket = Socket.connect(self.turtle.id, 161)
|
||||
end
|
||||
end
|
||||
|
||||
if not nowrap then
|
||||
script = 'turtle.run(' .. script .. ')'
|
||||
if socket then
|
||||
if not nowrap then
|
||||
script = 'turtle.run(' .. script .. ')'
|
||||
end
|
||||
if socket:write({ type = 'script', args = script }) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
socket = nil
|
||||
end
|
||||
socket:write({ type = 'script', args = script })
|
||||
self.notification:error('Unable to connect')
|
||||
end
|
||||
|
||||
function page:runScript(scriptName)
|
||||
@@ -270,6 +274,7 @@ end
|
||||
function page.tabs.turtles:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
page.turtle = event.selected
|
||||
multishell.setTitle(multishell.getCurrent(), page.turtle.label)
|
||||
if socket then
|
||||
socket:close()
|
||||
socket = nil
|
||||
|
||||
@@ -1447,7 +1447,7 @@ substitutionPage = UI.Page {
|
||||
{ heading = 'Name', key = 'display_name', width = UI.term.width-9 },
|
||||
{ heading = 'Qty', key = 'fQty', width = 5 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
sortColumn = 'display_name',
|
||||
height = UI.term.height-7,
|
||||
y = 7,
|
||||
},
|
||||
|
||||
@@ -31,8 +31,8 @@ if not controller:isValid() then
|
||||
controller = nil
|
||||
end
|
||||
|
||||
local chestAdapter = ChestAdapter({ direction = 'west', wrapSide = 'back' })
|
||||
local turtleChestAdapter = ChestAdapter({ direction = 'up', wrapSide = 'bottom' })
|
||||
local chestAdapter = ChestAdapter({ direction = 'north', wrapSide = 'colossalchests:colossalChest_0' })
|
||||
local turtleChestAdapter = ChestAdapter({ direction = 'down', wrapSide = 'top' })
|
||||
|
||||
local RESOURCE_FILE = 'usr/config/resources.db'
|
||||
local RECIPES_FILE = 'usr/etc/recipes.db'
|
||||
@@ -40,27 +40,10 @@ local RECIPES_FILE = 'usr/etc/recipes.db'
|
||||
local jobListGrid
|
||||
local craftingPaused = false
|
||||
local recipes = Util.readTable(RECIPES_FILE) or { }
|
||||
local resources = Util.readTable(RESOURCE_FILE) or { }
|
||||
local resources
|
||||
|
||||
Craft.setRecipes(recipes)
|
||||
|
||||
for _,r in pairs(resources) do
|
||||
r.maxDamage = nil
|
||||
r.displayName = nil
|
||||
r.count = nil
|
||||
r.lname = nil
|
||||
r.has_recipe = nil
|
||||
|
||||
if not r.ignoreDamage then
|
||||
r.ignoreDamage = nil
|
||||
end
|
||||
|
||||
if not r.auto then
|
||||
r.auto = nil
|
||||
end
|
||||
end
|
||||
Util.writeTable(RESOURCE_FILE, resources)
|
||||
|
||||
local function getItem(items, inItem, ignoreDamage)
|
||||
for _,item in pairs(items) do
|
||||
if item.name == inItem.name then
|
||||
@@ -76,7 +59,7 @@ end
|
||||
local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 2 then
|
||||
if #t[#t] > 8 then
|
||||
item.nbtHash = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
@@ -108,14 +91,6 @@ local function uniqueKey(item)
|
||||
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
end
|
||||
|
||||
local function getName(item)
|
||||
local detail = itemDB:get(itemDB:makeKey(item))
|
||||
if detail then
|
||||
return detail.displayName
|
||||
end
|
||||
return item.name .. ':' .. item.damage
|
||||
end
|
||||
|
||||
local function mergeResources(t)
|
||||
for _,v in pairs(resources) do
|
||||
local item = getItem(t, v)
|
||||
@@ -141,7 +116,7 @@ local function mergeResources(t)
|
||||
|
||||
for _,v in pairs(t) do
|
||||
if not v.displayName then
|
||||
v.displayName = getName(v)
|
||||
v.displayName = itemDB:getName(v)
|
||||
end
|
||||
v.lname = v.displayName:lower()
|
||||
end
|
||||
@@ -209,7 +184,7 @@ local function addCraftingRequest(item, craftList, count)
|
||||
local request = craftList[key]
|
||||
if not craftList[key] then
|
||||
request = { name = item.name, damage = item.damage, nbtHash = nbtHash, count = 0 }
|
||||
request.displayName = getName(request)
|
||||
request.displayName = itemDB:getName(request)
|
||||
craftList[key] = request
|
||||
end
|
||||
request.count = request.count + count
|
||||
@@ -221,7 +196,13 @@ local function craftItem(recipe, items, originalItem, craftList, count)
|
||||
return
|
||||
end
|
||||
|
||||
local toCraft = Craft.getCraftableAmount(recipe, count, items)
|
||||
local missing = { }
|
||||
local toCraft = Craft.getCraftableAmount(recipe, count, items, missing)
|
||||
|
||||
if missing.name then
|
||||
originalItem.status = string.format('%s missing', itemDB:getName(missing.name))
|
||||
originalItem.statusCode = 'missing'
|
||||
end
|
||||
|
||||
if toCraft > 0 then
|
||||
Craft.craftRecipe(recipe, toCraft, chestAdapter)
|
||||
@@ -265,7 +246,6 @@ local function craftItems(craftList, allItems)
|
||||
if controller:isCrafting(item) then
|
||||
item.status = '(crafting)'
|
||||
else
|
||||
|
||||
local count = item.count
|
||||
while count >= 1 do -- try to request smaller quantities until successful
|
||||
local s, m = pcall(function()
|
||||
@@ -311,6 +291,20 @@ local function jobMonitor(jobList)
|
||||
{ heading = 'Status', key = 'status', width = mon.width - 10 },
|
||||
},
|
||||
})
|
||||
|
||||
function jobListGrid:getRowTextColor(row, selected)
|
||||
|
||||
if row.status == '(no recipe)'then
|
||||
return colors.red
|
||||
elseif row.statusCode == 'missing' then
|
||||
return colors.yellow
|
||||
end
|
||||
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
end
|
||||
|
||||
local function getAutocraftItems()
|
||||
@@ -331,21 +325,17 @@ local function getItemWithQty(items, res, ignoreDamage)
|
||||
|
||||
local item = getItem(items, res, ignoreDamage)
|
||||
|
||||
if item then
|
||||
if item and ignoreDamage then
|
||||
local count = 0
|
||||
|
||||
if ignoreDamage then
|
||||
local count = 0
|
||||
|
||||
for _,v in pairs(items) do
|
||||
if item.name == v.name and item.nbtHash == v.nbtHash then
|
||||
if item.maxDamage > 0 or item.damage == v.damage then
|
||||
count = count + v.count
|
||||
end
|
||||
for _,v in pairs(items) do
|
||||
if item.name == v.name and item.nbtHash == v.nbtHash then
|
||||
if item.maxDamage > 0 or item.damage == v.damage then
|
||||
count = count + v.count
|
||||
end
|
||||
end
|
||||
|
||||
item.count = count
|
||||
end
|
||||
item.count = count
|
||||
end
|
||||
|
||||
return item
|
||||
@@ -362,13 +352,17 @@ local function watchResources(items)
|
||||
damage = res.damage,
|
||||
nbtHash = res.nbtHash,
|
||||
name = res.name,
|
||||
displayName = getName(res),
|
||||
displayName = itemDB:getName(res),
|
||||
count = 0
|
||||
}
|
||||
end
|
||||
|
||||
if res.limit and item.count > res.limit then
|
||||
chestAdapter:provide(res, item.count - res.limit, nil, config.trashDirection)
|
||||
chestAdapter:provide(
|
||||
{ name = item.name, damage = item.damage },
|
||||
item.count - res.limit,
|
||||
nil,
|
||||
config.trashDirection)
|
||||
|
||||
elseif res.low and item.count < res.low then
|
||||
if res.ignoreDamage then
|
||||
@@ -396,6 +390,27 @@ local function watchResources(items)
|
||||
return craftList
|
||||
end
|
||||
|
||||
local function loadResources()
|
||||
resources = Util.readTable(RESOURCE_FILE) or { }
|
||||
for k,v in pairs(resources) do
|
||||
Util.merge(v, splitKey(k))
|
||||
end
|
||||
end
|
||||
|
||||
local function saveResources()
|
||||
local t = { }
|
||||
|
||||
for k,v in pairs(resources) do
|
||||
v = Util.shallowCopy(v)
|
||||
v.name = nil
|
||||
v.damage = nil
|
||||
v.nbtHash = nil
|
||||
t[k] = v
|
||||
end
|
||||
|
||||
Util.writeTable(RESOURCE_FILE, t)
|
||||
end
|
||||
|
||||
local itemPage = UI.Page {
|
||||
backgroundColor = colors.lightGray,
|
||||
titleBar = UI.TitleBar {
|
||||
@@ -404,11 +419,9 @@ local itemPage = UI.Page {
|
||||
event = 'form_cancel',
|
||||
backgroundColor = colors.green
|
||||
},
|
||||
displayName = UI.Window {
|
||||
x = 2, y = 2, width = UI.term.width - 4, height = 3,
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 4, y = 5, height = 8, rex = -4,
|
||||
x = 2, y = 3, height = 8, rex = -4,
|
||||
margin = 1,
|
||||
[1] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
@@ -433,7 +446,7 @@ local itemPage = UI.Page {
|
||||
},
|
||||
[4] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
@@ -475,21 +488,11 @@ local itemPage = UI.Page {
|
||||
statusBar = UI.StatusBar { }
|
||||
}
|
||||
|
||||
function itemPage.displayName:draw()
|
||||
local item = self.parent.item
|
||||
local str = string.format('Name: %s\nDamage: %d', item.displayName, item.damage)
|
||||
if item.nbtHash then
|
||||
str = str .. string.format('\n%s', item.nbtHash)
|
||||
end
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(str)
|
||||
end
|
||||
|
||||
function itemPage:enable(item)
|
||||
self.item = item
|
||||
|
||||
self.form:setValues(item)
|
||||
self.titleBar.title = item.name
|
||||
self.titleBar.title = item.displayName or item.name
|
||||
|
||||
local devices = self.form[6].choices
|
||||
Util.clear(devices)
|
||||
@@ -518,7 +521,7 @@ function itemPage:eventHandler(event)
|
||||
elseif event.type == 'form_complete' then
|
||||
local values = self.form.values
|
||||
local keys = { 'name', 'auto', 'low', 'limit', 'damage',
|
||||
'nbtHash', 'ignoreDamage',
|
||||
'nbtHash',
|
||||
'rsControl', 'rsDevice', 'rsSide', }
|
||||
|
||||
local filtered = { }
|
||||
@@ -544,10 +547,11 @@ function itemPage:eventHandler(event)
|
||||
|
||||
if values.ignoreDamage == true then
|
||||
filtered.damage = 0
|
||||
filtered.ignoreDamage = true
|
||||
end
|
||||
|
||||
resources[uniqueKey(filtered)] = filtered
|
||||
Util.writeTable(RESOURCE_FILE, resources)
|
||||
saveResources()
|
||||
|
||||
UI:setPreviousPage()
|
||||
|
||||
@@ -778,7 +782,7 @@ local function learnRecipe(page)
|
||||
|
||||
Util.writeTable(RECIPES_FILE, recipes)
|
||||
|
||||
local displayName = getName(recipe[1])
|
||||
local displayName = itemDB:getName(recipe[1])
|
||||
|
||||
listingPage.statusBar.filter:setValue(displayName)
|
||||
listingPage.statusBar:timedStatus('Learned: ' .. displayName, 3)
|
||||
@@ -896,6 +900,9 @@ function craftPage:eventHandler(event)
|
||||
return true
|
||||
end
|
||||
|
||||
loadResources()
|
||||
clearGrid()
|
||||
|
||||
UI:setPages({
|
||||
listing = listingPage,
|
||||
item = itemPage,
|
||||
@@ -906,10 +913,7 @@ UI:setPages({
|
||||
UI:setPage(listingPage)
|
||||
listingPage:setFocus(listingPage.statusBar.filter)
|
||||
|
||||
clearGrid()
|
||||
jobMonitor()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
Event.onInterval(5, function()
|
||||
|
||||
@@ -922,10 +926,8 @@ Event.onInterval(5, function()
|
||||
|
||||
else
|
||||
local craftList = watchResources(items)
|
||||
jobListGrid:setValues(craftList)
|
||||
--jobListGrid:draw()
|
||||
--jobListGrid:sync()
|
||||
craftItems(craftList, items)
|
||||
jobListGrid:setValues(craftList)
|
||||
jobListGrid:update()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
261
apps/music.lua
Normal file
261
apps/music.lua
Normal file
@@ -0,0 +1,261 @@
|
||||
require = requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local UI = require('ui')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Music')
|
||||
|
||||
local radio = device.drive or error('No drive attached')
|
||||
if radio.side ~= 'top' and radio.side ~= 'bottom' then
|
||||
error('Disk drive must be above or below turtle')
|
||||
end
|
||||
|
||||
if not turtle then
|
||||
error('This program can only be run on a turtle')
|
||||
end
|
||||
|
||||
if not device.monitor then
|
||||
error('Monitor must be attached (3 wide x 1 tall')
|
||||
end
|
||||
|
||||
local monitor = UI.Device({
|
||||
deviceType = 'monitor',
|
||||
textScale = 0.5,
|
||||
})
|
||||
|
||||
UI:setDefaultDevice(monitor)
|
||||
|
||||
local page = UI.Page({
|
||||
volume = 15,
|
||||
stationName = UI.Text({
|
||||
y = 2,
|
||||
x = 2,
|
||||
width = monitor.width - 14,
|
||||
height = 3,
|
||||
backgroundColor = colors.brown,
|
||||
}),
|
||||
seek = UI.Button({
|
||||
y = 7,
|
||||
x = 13,
|
||||
height = 3,
|
||||
event = 'seek',
|
||||
text = ' >> ',
|
||||
}),
|
||||
play = UI.Button({
|
||||
y = 7,
|
||||
x = 2,
|
||||
height = 3,
|
||||
event = 'play',
|
||||
text = '> / ll',
|
||||
}),
|
||||
louder = UI.Button({
|
||||
y = 7,
|
||||
x = monitor.width - 15,
|
||||
width = 3,
|
||||
height = 3,
|
||||
event = 'louder',
|
||||
text = '+',
|
||||
}),
|
||||
quiet = UI.Button({
|
||||
y = 7,
|
||||
x = monitor.width - 20,
|
||||
event = 'quiet',
|
||||
width = 3,
|
||||
height = 3,
|
||||
text = '-',
|
||||
}),
|
||||
volumeDisplay = UI.Text({
|
||||
y = 3,
|
||||
x = monitor.width - 9,
|
||||
width = 4,
|
||||
}),
|
||||
volume1 = UI.Window({
|
||||
y = monitor.height - 1,
|
||||
x = monitor.width - 8,
|
||||
height = 1,
|
||||
width = 1,
|
||||
color = colors.white
|
||||
}),
|
||||
volume2 = UI.Window({
|
||||
y = monitor.height - 2,
|
||||
x = monitor.width - 7,
|
||||
height = 2,
|
||||
width = 1,
|
||||
color = colors.white
|
||||
}),
|
||||
volume3 = UI.Window({
|
||||
y = monitor.height - 3,
|
||||
x = monitor.width - 6,
|
||||
height = 3,
|
||||
width = 1,
|
||||
color = colors.yellow
|
||||
}),
|
||||
volume4 = UI.Window({
|
||||
y = monitor.height - 4,
|
||||
x = monitor.width - 5,
|
||||
height = 4,
|
||||
width = 1,
|
||||
color = colors.yellow,
|
||||
}),
|
||||
volume5 = UI.Window({
|
||||
y = monitor.height - 5,
|
||||
x = monitor.width - 4,
|
||||
height = 5,
|
||||
width = 1,
|
||||
color = colors.orange,
|
||||
}),
|
||||
volume6 = UI.Window({
|
||||
y = monitor.height - 6,
|
||||
x = monitor.width - 3,
|
||||
height = 6,
|
||||
width = 1,
|
||||
color = colors.orange,
|
||||
}),
|
||||
volume7 = UI.Window({
|
||||
y = monitor.height - 7,
|
||||
x = monitor.width - 2,
|
||||
height = 7,
|
||||
width = 1,
|
||||
color = colors.red,
|
||||
}),
|
||||
volume8 = UI.Window({
|
||||
y = monitor.height - 8,
|
||||
x = monitor.width - 1,
|
||||
height = 8,
|
||||
width = 1,
|
||||
color = colors.red,
|
||||
})
|
||||
})
|
||||
|
||||
page.volumeControls = {
|
||||
page.volume1, page.volume2,
|
||||
page.volume3, page.volume4,
|
||||
page.volume5, page.volume6,
|
||||
page.volume7, page.volume8,
|
||||
}
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'play' then
|
||||
self:play(not self.playing)
|
||||
elseif event.type == 'seek' then
|
||||
self:seek()
|
||||
self:play(true)
|
||||
elseif event.type == 'louder' then
|
||||
if self.playing then
|
||||
self:setVolume(self.volume + 1)
|
||||
end
|
||||
elseif event.type == 'quiet' then
|
||||
if self.playing then
|
||||
self:setVolume(self.volume - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function page:setVolume(volume, displayOnly)
|
||||
volume = math.min(volume, 15)
|
||||
volume = math.max(volume, 1)
|
||||
self.volume = volume
|
||||
volume = math.ceil(volume / 2)
|
||||
|
||||
for i = 1, volume do
|
||||
self.volumeControls[i].backgroundColor =
|
||||
self.volumeControls[i].color
|
||||
end
|
||||
for i = volume + 1, #self.volumeControls do
|
||||
self.volumeControls[i].backgroundColor = colors.black
|
||||
end
|
||||
for i = 1, #self.volumeControls do
|
||||
self.volumeControls[i]:clear()
|
||||
end
|
||||
local percent = math.ceil(self.volume / 15 * 100)
|
||||
self.volumeDisplay.value = percent .. '%'
|
||||
self.volumeDisplay:draw()
|
||||
end
|
||||
|
||||
function page:seek()
|
||||
|
||||
local actions = {
|
||||
top = {
|
||||
suck = turtle.suckUp,
|
||||
drop = turtle.dropUp,
|
||||
},
|
||||
bottom = {
|
||||
suck = turtle.suckDown,
|
||||
drop = turtle.dropDown,
|
||||
},
|
||||
}
|
||||
|
||||
local slot = turtle.selectOpenSlot()
|
||||
actions[radio.side].suck()
|
||||
repeat
|
||||
slot = slot + 1
|
||||
if (slot > 16) then
|
||||
slot = 1
|
||||
end
|
||||
until turtle.getItemCount(slot) >= 1
|
||||
turtle.select(slot)
|
||||
actions[radio.side].drop()
|
||||
self:updateStationName()
|
||||
end
|
||||
|
||||
function page:play(onOff)
|
||||
self.playing = onOff
|
||||
if self.playing then
|
||||
|
||||
if not radio.hasAudio() then
|
||||
self:seek()
|
||||
end
|
||||
|
||||
self:updateStationName()
|
||||
radio.playAudio()
|
||||
|
||||
Event.addNamedTimer('songTimer', 180, false, function()
|
||||
if self.playing then
|
||||
self:seek()
|
||||
self:play(true)
|
||||
self:sync()
|
||||
end
|
||||
end)
|
||||
|
||||
else
|
||||
radio.stopAudio()
|
||||
end
|
||||
end
|
||||
|
||||
function page.stationName:draw()
|
||||
self:clear()
|
||||
self:centeredWrite(2, self.value)
|
||||
end
|
||||
|
||||
function page:updateStationName()
|
||||
local title = radio.getAudioTitle()
|
||||
|
||||
if title then
|
||||
self.stationName.value = title
|
||||
self.stationName:draw()
|
||||
end
|
||||
end
|
||||
|
||||
Event.onInterval(1, function()
|
||||
if not page.playing then
|
||||
if page.stationName.value == '' then
|
||||
page:updateStationName()
|
||||
else
|
||||
page.stationName.value = ''
|
||||
page.stationName:draw()
|
||||
end
|
||||
page:sync()
|
||||
end
|
||||
end)
|
||||
|
||||
page:play(true)
|
||||
page:setVolume(page.volume, true)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
turtle.status = 'Jamming'
|
||||
UI:pullEvents()
|
||||
turtle.status = 'idle'
|
||||
page:play(false)
|
||||
|
||||
UI.term:reset()
|
||||
125
apps/pickup.lua
125
apps/pickup.lua
@@ -1,38 +1,34 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local GPS = require('gps')
|
||||
local Logger = require('logger')
|
||||
local MEProvider = require('meProvider')
|
||||
local Point = require('point')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
local Event = require('event')
|
||||
local GPS = require('gps')
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local Point = require('point')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
|
||||
if not device.wireless_modem then
|
||||
error('Modem is required')
|
||||
end
|
||||
|
||||
Logger.setWirelessLogging()
|
||||
|
||||
if not turtle then
|
||||
error('Can only be run on a turtle')
|
||||
end
|
||||
|
||||
local blocks = { }
|
||||
local meProvider = MEProvider()
|
||||
local items = { }
|
||||
|
||||
local pickups = Util.readTable('pickup.tbl') or { }
|
||||
local cells = Util.readTable('cells.tbl') or { }
|
||||
local refills = Util.readTable('refills.tbl') or { }
|
||||
local fluids = Util.readTable('fluids.tbl') or { }
|
||||
local chestPt = turtle.loadLocation('chest')
|
||||
local chargePt = turtle.loadLocation('charge')
|
||||
local locations = Util.readTable('/usr/config/pickup') or {
|
||||
pickups = { },
|
||||
cells = { },
|
||||
refills = { },
|
||||
fluids = { },
|
||||
}
|
||||
|
||||
local fuel = {
|
||||
item = {
|
||||
id = 'minecraft:coal',
|
||||
dmg = 0,
|
||||
name = 'minecraft:coal',
|
||||
damage = 0,
|
||||
},
|
||||
qty = 64
|
||||
}
|
||||
@@ -51,14 +47,18 @@ turtle.setMoveCallback(function(action, pt)
|
||||
end)
|
||||
|
||||
function refuel()
|
||||
if turtle.getFuelLevel() < 5000 then
|
||||
if turtle.getFuelLevel() < 5000 and locations.dropPt then
|
||||
print('refueling')
|
||||
turtle.status = 'refueling'
|
||||
gotoPoint(chestPt, true)
|
||||
dropOff(chestPt)
|
||||
gotoPoint(locations.dropPt, true)
|
||||
dropOff(locations.dropPt)
|
||||
local chestAdapter = ChestAdapter({
|
||||
wrapSide = 'bottom',
|
||||
direction = 'up',
|
||||
})
|
||||
while turtle.getFuelLevel() < 5000 do
|
||||
turtle.select(1)
|
||||
meProvider:provide(fuel.item, fuel.qty, 1)
|
||||
chestAdapter:provide(fuel.item, fuel.qty, 1)
|
||||
turtle.refuel(64)
|
||||
print(turtle.getFuelLevel())
|
||||
os.sleep(1)
|
||||
@@ -71,7 +71,7 @@ function pickUp(pt)
|
||||
gotoPoint(pt, true)
|
||||
while true do
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(chestPt)
|
||||
dropOff(locations.dropPt)
|
||||
gotoPoint(pt, true)
|
||||
end
|
||||
turtle.select(1)
|
||||
@@ -85,16 +85,17 @@ function dropOff(pt)
|
||||
if turtle.selectSlotWithItems() then
|
||||
gotoPoint(pt, true)
|
||||
turtle.emptyInventory(turtle.dropDown)
|
||||
if pt == chestPt then
|
||||
if pt == locations.dropPt then
|
||||
print('refreshing items')
|
||||
items = meProvider:refresh()
|
||||
chestAdapter = ChestAdapter()
|
||||
items = chestAdapter:refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function gotoPoint(pt, doDetect)
|
||||
slots = turtle.getInventory()
|
||||
while not turtle.pathfind(pt, blocks) do
|
||||
while not turtle.pathfind(pt, { blocks = blocks }) do
|
||||
if turtle.abort then
|
||||
error('aborted')
|
||||
end
|
||||
@@ -110,7 +111,7 @@ end
|
||||
|
||||
function checkCell(pt)
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(chestPt)
|
||||
dropOff(locations.dropPt)
|
||||
end
|
||||
|
||||
print('checking cell')
|
||||
@@ -123,7 +124,7 @@ function checkCell(pt)
|
||||
print('charging cell')
|
||||
turtle.selectOpenSlot()
|
||||
turtle.digDown()
|
||||
gotoPoint(chargePt, true)
|
||||
gotoPoint(locations.chargePt, true)
|
||||
turtle.dropDown()
|
||||
os.sleep(energy / 20000)
|
||||
turtle.suckDown()
|
||||
@@ -152,12 +153,13 @@ function fluid(points)
|
||||
end
|
||||
|
||||
function refill(entry)
|
||||
dropOff(chestPt)
|
||||
dropOff(locations.dropPt)
|
||||
|
||||
turtle.status = 'refilling'
|
||||
gotoPoint(chestPt)
|
||||
gotoPoint(locations.dropPt)
|
||||
local chestAdapter = ChestAdapter()
|
||||
for _,item in pairs(entry.items) do
|
||||
meProvider:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
|
||||
chestAdapter:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
|
||||
end
|
||||
|
||||
if turtle.selectSlotWithItems() then
|
||||
@@ -179,7 +181,7 @@ function oldRefill(points)
|
||||
end
|
||||
end
|
||||
dropOff(points.source)
|
||||
dropOff(chestPt)
|
||||
dropOff(locations.dropPt)
|
||||
end
|
||||
|
||||
local function makeKey(pt)
|
||||
@@ -199,8 +201,8 @@ local function pickupHost(socket)
|
||||
|
||||
if data.type == 'pickup' then
|
||||
local key = makeKey(data.point)
|
||||
pickups[key] = data.point
|
||||
Util.writeTable('pickup.tbl', pickups)
|
||||
locations.pickups[key] = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'items' then
|
||||
@@ -208,41 +210,36 @@ local function pickupHost(socket)
|
||||
|
||||
elseif data.type == 'refill' then
|
||||
local key = makeKey(data.entry.point)
|
||||
refills[key] = data.entry
|
||||
Util.writeTable('refills.tbl', refills)
|
||||
locations.refills[key] = data.entry
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'setPickup' then
|
||||
chestPt = data.point
|
||||
-- fix
|
||||
turtle.storeLocation('chest', chestPt)
|
||||
locations.dropPt = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'setRecharge' then
|
||||
chargePt = data.point
|
||||
-- fix
|
||||
turtle.storeLocation('charge', chargePt)
|
||||
locations.chargePt = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'charge' then
|
||||
local key = makeKey(data.point)
|
||||
cells[key] = data.point
|
||||
Util.writeTable('cells.tbl', cells)
|
||||
locations.cells[key] = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'fluid' then
|
||||
|
||||
elseif data.type == 'clear' then
|
||||
local key = makeKey(data.point)
|
||||
refills[key] = nil
|
||||
cells[key] = nil
|
||||
fluids[key] = nil
|
||||
pickups[key] = nil
|
||||
locations.refills[key] = nil
|
||||
locations.cells[key] = nil
|
||||
locations.fluids[key] = nil
|
||||
locations.pickups[key] = nil
|
||||
|
||||
Util.writeTable('refills.tbl', refills)
|
||||
Util.writeTable('cells.tbl', cells)
|
||||
Util.writeTable('fluids.tbl', fluids)
|
||||
Util.writeTable('pickup.tbl', pickups)
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
|
||||
socket:write( { type = "response", response = 'cleared' })
|
||||
else
|
||||
@@ -303,15 +300,22 @@ end
|
||||
|
||||
Event.addRoutine(function()
|
||||
|
||||
if not turtle.enableGPS() then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
|
||||
refuel()
|
||||
|
||||
while true do
|
||||
if chestPt then
|
||||
eachClosestEntry(pickups, pickUp)
|
||||
eachEntry(refills, refill)
|
||||
if locations.dropPt then
|
||||
eachClosestEntry(locations.pickups, pickUp)
|
||||
eachEntry(locations.refills, refill)
|
||||
refuel()
|
||||
end
|
||||
eachEntry(fluids, fluid)
|
||||
if chargePt then
|
||||
eachEntry(cells, checkCell)
|
||||
dropOff(locations.dropPt)
|
||||
eachEntry(locations.fluids, fluid)
|
||||
if locations.chargePt then
|
||||
eachEntry(locations.cells, checkCell)
|
||||
end
|
||||
print('sleeping')
|
||||
turtle.status = 'sleeping'
|
||||
@@ -327,11 +331,6 @@ end)
|
||||
|
||||
turtle.run(function()
|
||||
|
||||
if not turtle.enableGPS() then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
|
||||
refuel()
|
||||
Event.pullEvents()
|
||||
|
||||
end)
|
||||
|
||||
@@ -18,11 +18,12 @@ local mainPage = UI.Page({
|
||||
menu = UI.Menu({
|
||||
centered = true,
|
||||
y = 2,
|
||||
height = 8,
|
||||
menuItems = {
|
||||
{ prompt = 'Pickup', event = 'pickup', help = 'Pickup items from this location' },
|
||||
{ prompt = 'Charge cell', event = 'charge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Refill', event = 'refill', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set pickup location', event = 'setPickup', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set drop off location', event = 'setPickup', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set recharge location', event = 'setRecharge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Clear', event = 'clear', help = 'Remove this location' },
|
||||
},
|
||||
@@ -89,7 +90,7 @@ local function sendCommand(cmd)
|
||||
end
|
||||
|
||||
local function getPoint()
|
||||
local gpt = GPS.getPoint()
|
||||
local gpt = GPS.getPoint(2)
|
||||
if not gpt then
|
||||
mainPage.statusBar:timedStatus('Unable to get location', 3)
|
||||
end
|
||||
|
||||
136
apps/shapes.lua
136
apps/shapes.lua
@@ -251,143 +251,19 @@ local levelScript = [[
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Point = require('point')
|
||||
local Level = require('turtle.level')
|
||||
local Util = require('util')
|
||||
|
||||
local checkedNodes = { }
|
||||
local nodes = { }
|
||||
local box = { }
|
||||
|
||||
local function inBox(pt, box)
|
||||
return pt.x >= box.x and
|
||||
pt.y >= box.y and
|
||||
pt.z >= box.z and
|
||||
pt.x <= box.ex and
|
||||
pt.y <= box.ey and
|
||||
pt.z <= box.ez
|
||||
end
|
||||
|
||||
local function toKey(pt)
|
||||
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
||||
end
|
||||
|
||||
local function addNode(node)
|
||||
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
||||
|
||||
if inBox(testNode, box) then
|
||||
local key = toKey(testNode)
|
||||
if not checkedNodes[key] then
|
||||
nodes[key] = testNode
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dig(action)
|
||||
|
||||
local directions = {
|
||||
top = 'up',
|
||||
bottom = 'down',
|
||||
}
|
||||
|
||||
-- convert to up, down, north, south, east, west
|
||||
local direction = directions[action.side] or
|
||||
turtle.getHeadingInfo(turtle.point.heading).direction
|
||||
|
||||
local hi = turtle.getHeadingInfo(direction)
|
||||
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
|
||||
if inBox(node, box) then
|
||||
|
||||
local key = toKey(node)
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
if action.dig() then
|
||||
addNode(node)
|
||||
repeat until not action.dig() -- sand, etc
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function move(action)
|
||||
if action == 'turn' then
|
||||
dig(turtle.getAction('forward'))
|
||||
elseif action == 'up' then
|
||||
dig(turtle.getAction('up'))
|
||||
elseif action == 'down' then
|
||||
dig(turtle.getAction('down'))
|
||||
elseif action == 'back' then
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('down'))
|
||||
end
|
||||
end
|
||||
|
||||
local function getAdjacentPoint(pt)
|
||||
local t = { }
|
||||
table.insert(t, pt)
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local heading
|
||||
if i < 4 then
|
||||
heading = (hi.heading + 2) % 4
|
||||
end
|
||||
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
||||
end
|
||||
|
||||
return Point.closest2(turtle.getPoint(), t)
|
||||
end
|
||||
|
||||
local function level()
|
||||
|
||||
box.x = math.min(data.startPt.x, data.endPt.x)
|
||||
box.y = math.min(data.startPt.y, data.endPt.y)
|
||||
box.z = math.min(data.startPt.z, data.endPt.z)
|
||||
box.ex = math.max(data.startPt.x, data.endPt.x)
|
||||
box.ey = math.max(data.startPt.y, data.endPt.y)
|
||||
box.ez = math.max(data.startPt.z, data.endPt.z)
|
||||
|
||||
turtle.pathfind(data.firstPt)
|
||||
|
||||
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
|
||||
turtle.setMoveCallback(move)
|
||||
|
||||
repeat
|
||||
local key = toKey(turtle.point)
|
||||
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
dig(turtle.getAction('down'))
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('forward'))
|
||||
|
||||
print(string.format('%d nodes remaining', Util.size(nodes)))
|
||||
|
||||
if Util.size(nodes) == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
local node = Point.closest2(turtle.point, nodes)
|
||||
node = getAdjacentPoint(node)
|
||||
if not turtle.gotoPoint(node) then
|
||||
break
|
||||
end
|
||||
until turtle.abort
|
||||
|
||||
turtle.resetState()
|
||||
end
|
||||
|
||||
local s, m = turtle.run(function()
|
||||
turtle.status = 'Leveling'
|
||||
|
||||
if turtle.enableGPS() then
|
||||
|
||||
local pt = Util.shallowCopy(turtle.point)
|
||||
local s, m = pcall(level)
|
||||
local s, m = pcall(function()
|
||||
Level(data.startPt, data.endPt, data.firstPt)
|
||||
end)
|
||||
|
||||
turtle.pathfind(pt)
|
||||
|
||||
if not s and m then
|
||||
@@ -446,7 +322,7 @@ end
|
||||
|
||||
function page:runFunction(id, script)
|
||||
|
||||
Util.writeFile('script.tmp', script)
|
||||
--Util.writeFile('script.tmp', script)
|
||||
self.notification:info('Connecting')
|
||||
local fn, msg = loadstring(script, 'script')
|
||||
if not fn then
|
||||
|
||||
3894
etc/recipes.db
3894
etc/recipes.db
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user