1
0
mirror of https://github.com/kepler155c/opus synced 2025-10-15 07:47:40 +00:00

major directory reorganize

This commit is contained in:
kepler155c@gmail.com
2017-05-20 18:27:26 -04:00
parent 7954c79d66
commit c8147ef9e8
85 changed files with 67 additions and 59 deletions

View File

@@ -68,7 +68,7 @@ function blockDB:seedDB(dir)
return res
end
local f = fs.open(fs.combine(dir, 'blockIds.csv'), "r")
local f = fs.open(fs.combine('sys/etc', 'blockIds.csv'), "r")
if not f then
error('unable to read blockIds.csv')

View File

@@ -3,10 +3,10 @@ local Util = require('util')
local Config = { }
Config.load = function(fname, data)
local filename = '/config/' .. fname
local filename = 'usr/config/' .. fname
if not fs.exists('/config') then
fs.makeDir('/config')
if not fs.exists('usr/config') then
fs.makeDir('usr/config')
end
if not fs.exists(filename) then
@@ -17,7 +17,7 @@ Config.load = function(fname, data)
end
Config.update = function(fname, data)
local filename = '/config/' .. fname
local filename = 'usr/config/' .. fname
Util.writeTable(filename, data)
end

View File

@@ -146,7 +146,7 @@ local function trusted(msg, port)
return true
end
local trustList = Util.readTable('.known_hosts') or { }
local trustList = Util.readTable('usr/.known_hosts') or { }
local pubKey = trustList[msg.shost]
if pubKey then

View File

@@ -202,7 +202,7 @@ function Manager:configure(appName, ...)
textScale = { arg = 't', type = 'number',
desc = 'Text scale' },
}
local defaults = Util.loadTable('/config/' .. appName) or { }
local defaults = Util.loadTable('usr/config/' .. appName) or { }
if not defaults.device then
defaults.device = { }
end
@@ -3321,7 +3321,7 @@ function UI.NftImage:setImage(image)
self.image = image
end
UI:loadTheme('config/ui.theme')
UI:loadTheme('usr/config/ui.theme')
if os.getVersion() >= 1.79 then
UI:loadTheme('sys/etc/ext.theme')
end

View File

@@ -0,0 +1,8 @@
{
icon = "\030 \0310=\0300 \030 XX\0300\031f \030 \
\030 \031f \0300 \030 \
\030 \031f \0310o \031f \0310o\031f ",
category = "System",
title = "AppStore",
run = "Appstore.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0304\031f \030 \0311e\
\030f\031f \0304 \030 \0311ee\031f \
\030f\031f \0304 \030 \0311e\031f ",
title = "Events",
category = "System",
run = "Events.lua",
}

8
sys/apps/.overview/Files Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\0300\0317==\031 \0307 \
\0300\0317====\
\0300\0317====",
title = "Files",
category = "Apps",
run = "Files.lua",
}

8
sys/apps/.overview/Help Normal file
View File

@@ -0,0 +1,8 @@
{
icon = " \031d?\031 \
\031d?\031 \
\031d?",
title = "Help",
category = "Apps",
run = "Help.lua",
}

8
sys/apps/.overview/Lua Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\030f \
\030f\0310lua>\031 \
\030f ",
title = "Lua",
category = "Apps",
run = "Lua.lua",
}

View File

@@ -0,0 +1,9 @@
{
title = "Network",
category = "Apps",
requires = "wireless_modem",
icon = "\0304 \030 \
\030f \0304 \0307 \030 \031 \031f)\
\030f \0304 \0307 \030 \031f)",
run = "Network.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0304 \030 \
\030f \0304 \0307 \030 \031 \031f_\
\030f \0304 \0307 \030 \031f/",
title = "Devices",
category = "System",
run = "Peripherals.lua",
}

View File

@@ -0,0 +1,9 @@
{
title = "Scripts",
category = "Apps",
requires = "wireless_modem",
icon = "\0300\0317if\031 \0307 \
\0300\0317turt\
\0300\0317retu",
run = "Script.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = " \0307\031f| \
\0307\031f---o\030 \031 \
\0307\031f| ",
title = "System",
category = "System",
run = "System.lua",
}

8
sys/apps/.overview/Tabs Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\0307 \0303\0317__\0307\031 \
\0303 \
\0303 ",
title = "Tabs",
category = "System",
run = "Tabs.lua",
}

View File

@@ -0,0 +1,5 @@
{
category = "Apps",
title = "Turtles",
run = "Turtles.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\030f\0310You \031 \
\030f\0310Ther\030 \031 \
\030f\0314?\031f \031 \030 ",
title = "Adventure",
category = "Games",
run = "rom/programs/fun/adventure",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0317_____\
\030e\031c###\0308\0317=\030e\031c#\
\030e\031c#\0307\031f.\030e\031c###",
title = "Builder",
category = "Apps",
run = "builder.lua",
}

8
sys/apps/.overview/dj Normal file
View File

@@ -0,0 +1,8 @@
{
icon = " \030f \
\030f \0307 \
\030f \0307 \0300 ",
title = "DJ",
category = "Games",
run = "/rom/programs/fun/dj",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\030f \0302 \
\0309 \0302 \0301 \
\030e \0309 \0301 ",
title = "Falling",
category = "Games",
run = "rom/programs/pocket/falling",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0304\031f \030f\0310o..\0304\031f \
\0304\031f \030f\0310.o.\0304\031f \
\0304\031f - ",
title = "Reboot",
category = "System",
run = "rom/programs/reboot",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\030 \031f \031b \031foo \
\030 \031f \030e\031b \030 \031f/\
\030 \031b \030e \030 \031f\\",
category = "Apps",
title = "Recorder",
run = "recorder.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0307 \0308 \0307 \
\0308\031b> \030b\0310>\0308\0318 \
\0307 ",
title = "Redirection",
category = "Games",
run = "rom/programs/fun/advanced/redirection",
}

8
sys/apps/.overview/shell Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\0304 \030 \
\0304 \030f\0314> \0310_\031 \
\0304 \030f \030 ",
title = "Shell",
category = "Apps",
run = "shell",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0304\031f \
\0304\031f \030f\0310zz\031 \
\0304\031f \030f ",
title = "Shutdown",
category = "System",
run = "/rom/programs/shutdown",
}

View File

@@ -0,0 +1,8 @@
{
icon = " \0315\\\030 \031 \
\0304\031f _ \030 \031c/\0315\\\
\0304 ",
title = "Miner",
category = "Apps",
run = "simpleMiner.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0318/\030f\031 \030 \0318\\\
\030f \0308\0319o\030f\031 \
\0318\\\030f\031 \030 \0318/",
title = "Activity",
category = "Apps",
run = "storageActivity.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0307 \
\0307 \0308\0311 \0305 \0308\031 \0307 \0308 \0301 \
\0307 ",
title = "Storage",
category = "Apps",
run = "storageManager.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = " \0314>\0310_\
\031f)))\031 \
\0314>\0310_\031 ",
title = "Telnet",
category = "Apps",
run = "telnet.lua",
}

View File

@@ -0,0 +1,8 @@
{
icon = "\0301\03171\03180\030 \031 \
\0301\03181\030 \031 \
\0301\03170\03180\03171\0307\031f>",
title = "Update",
category = "System",
run = "update.lua",
}

8
sys/apps/.overview/vnc Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\
\031e\\\031 \031e/\031dn\
\031e\\/\031 \0319c",
title = "VNC",
category = "Apps",
run = "vnc.lua",
}

8
sys/apps/.overview/worm Normal file
View File

@@ -0,0 +1,8 @@
{
icon = "\030d \030 \030e \030 \
\030d \030 \
\030d ",
title = "Worm",
category = "Games",
run = "/rom/programs/fun/worm",
}

386
sys/apps/Appstore.lua Normal file
View File

@@ -0,0 +1,386 @@
require = requireInjector(getfenv(1))
local Util = require('util')
local class = require('class')
local UI = require('ui')
local Event = require('event')
local sandboxEnv = Util.shallowCopy(getfenv(1))
setmetatable(sandboxEnv, { __index = _G })
multishell.setTitle(multishell.getCurrent(), 'App Store')
UI:configure('Appstore', ...)
local sources = {
{ text = "STD Default",
event = 'source',
url = "http://pastebin.com/raw/zVws7eLq" }, --stock
{ text = "Discover",
event = 'source',
generateName = true,
url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95
{ text = "Opus",
event = 'source',
url = "http://pastebin.com/raw/ajQ91Rmn" },
}
shell.setDir('/apps')
function downloadApp(app)
local h
if type(app.url) == "table" then
h = contextualGet(app.url[1])
else
h = http.get(app.url)
end
if h then
local contents = h.readAll()
h:close()
return contents
end
end
function runApp(app, checkExists, ...)
local path, fn
local args = { ... }
if checkExists and fs.exists(fs.combine('/apps', app.name)) then
path = fs.combine('/apps', app.name)
else
local program = downloadApp(app)
fn = function()
if not program then
error('Failed to download')
end
local fn = loadstring(program, app.name)
if not fn then
error('Failed to download')
end
setfenv(fn, sandboxEnv)
fn(unpack(args))
end
end
multishell.openTab({
title = app.name,
env = sandboxEnv,
path = path,
fn = fn,
focused = true,
})
return true, 'Running program'
end
local installApp = function(app)
local program = downloadApp(app)
if not program then
return false, "Failed to download"
end
local fullPath = fs.combine('/apps', app.name)
Util.writeFile(fullPath, program)
return true, 'Installed as ' .. fullPath
end
local viewApp = function(app)
local program = downloadApp(app)
if not program then
return false, "Failed to download"
end
Util.writeFile('/.source', program)
shell.openForegroundTab('edit /.source')
return true
end
local getSourceListing = function(source)
local contents = http.get(source.url)
if contents then
local fn = loadstring(contents.readAll(), source.text)
contents.close()
local env = { std = { } }
setmetatable(env, { __index = _G })
setfenv(fn, env)
fn()
if env.contextualGet then
contextualGet = env.contextualGet
end
source.storeURLs = env.std.storeURLs
source.storeCatagoryNames = env.std.storeCatagoryNames
if source.storeURLs and source.storeCatagoryNames then
for k,v in pairs(source.storeURLs) do
if source.generateName then
v.name = v.title:match('(%w+)')
if not v.name or #v.name == 0 then
v.name = tostring(k)
else
v.name = v.name:lower()
end
else
v.name = k
end
v.categoryName = source.storeCatagoryNames[v.catagory]
v.ltitle = v.title:lower()
end
end
end
end
local appPage = UI.Page({
backgroundColor = UI.ViewportWindow.defaults.backgroundColor,
menuBar = UI.MenuBar({
showBackButton = not os.isPocket(),
buttons = {
{ text = 'Install', event = 'install' },
{ text = 'Run', event = 'run' },
{ text = 'View', event = 'view' },
{ text = 'Remove', event = 'uninstall', name = 'removeButton' },
},
}),
container = UI.Window({
x = 2,
y = 3,
height = UI.term.height - 3,
width = UI.term.width - 2,
viewport = UI.ViewportWindow(),
}),
notification = UI.Notification(),
accelerators = {
q = 'back',
backspace = 'back',
},
})
function appPage.container.viewport:draw()
local app = self.parent.parent.app
local str = string.format(
'By: %s\nCategory: %s\nFile name: %s\n\n%s',
app.creator, app.categoryName, app.name, app.description)
self:clear()
local y = self:wrappedWrite(1, 1, app.title, self.width, nil, colors.yellow)
self.height = self:wrappedWrite(1, y, str, self.width)
if appPage.notification.enabled then
appPage.notification:draw()
end
end
function appPage:enable(source, app)
self.source = source
self.app = app
UI.Page.enable(self)
self.container.viewport:setScrollPosition(0)
if fs.exists(fs.combine('/apps', app.name)) then
self.menuBar.removeButton:enable('Remove')
else
self.menuBar.removeButton:disable('Remove')
end
end
function appPage:eventHandler(event)
if event.type == 'back' then
UI:setPreviousPage()
elseif event.type == 'run' then
self.notification:info('Running program', 3)
self:sync()
runApp(self.app, true)
elseif event.type == 'view' then
self.notification:info('Downloading program', 3)
self:sync()
viewApp(self.app)
elseif event.type == 'uninstall' then
if self.app.runOnly then
s,m = runApp(self.app, false, 'uninstall')
else
fs.delete(fs.combine('/apps', self.app.name))
self.notification:success("Uninstalled " .. self.app.name, 3)
self:focusFirst(self)
self.menuBar.removeButton:disable('Remove')
self.menuBar:draw()
os.unregisterApp(fs.combine('/apps', self.app.name))
end
elseif event.type == 'install' then
self.notification:info("Installing", 3)
self:sync()
local s, m
if self.app.runOnly then
s,m = runApp(self.app, false)
else
s,m = installApp(self.app)
end
if s then
self.notification:success(m, 3)
if not self.app.runOnly then
self.menuBar.removeButton:enable('Remove')
self.menuBar:draw()
local category = 'Apps'
if self.app.catagoryName == 'Game' then
category = 'Games'
end
os.registerApp({
run = fs.combine('/apps', self.app.name),
title = self.app.title,
category = category,
icon = self.app.icon,
})
end
else
self.notification:error(m, 3)
end
else
return UI.Page.eventHandler(self, event)
end
return true
end
local categoryPage = UI.Page({
menuBar = UI.MenuBar({
buttons = {
{ text = 'Catalog', event = 'dropdown', dropdown = 'sourceMenu' },
{ text = 'Category', event = 'dropdown', dropdown = 'categoryMenu' },
},
}),
sourceMenu = UI.DropMenu({
buttons = sources,
}),
grid = UI.ScrollingGrid({
y = 2,
height = UI.term.height - 2,
columns = {
{ heading = 'Title', key = 'title' },
},
sortColumn = 'title',
autospace = true,
}),
statusBar = UI.StatusBar(),
accelerators = {
l = 'lua',
q = 'quit',
},
})
function categoryPage:setCategory(source, name, index)
self.grid.values = { }
for k,v in pairs(source.storeURLs) do
if index == 0 or index == v.catagory then
table.insert(self.grid.values, v)
end
end
self.statusBar:setStatus(string.format('%s: %s', source.text, name))
self.grid:update()
self.grid:setIndex(1)
end
function categoryPage:setSource(source)
if not source.categoryMenu then
self.statusBar:setStatus('Loading...')
self.statusBar:draw()
self:sync()
getSourceListing(source)
if not source.storeURLs then
error('Unable to download application list')
end
local buttons = { }
for k,v in Util.spairs(source.storeCatagoryNames,
function(a, b) return a:lower() < b:lower() end) do
if v ~= 'Operating System' then
table.insert(buttons, {
text = v,
event = 'category',
index = k,
})
end
end
source.categoryMenu = UI.DropMenu({
y = 2,
x = 1,
buttons = buttons,
})
source.index, source.name = Util.first(source.storeCatagoryNames)
categoryPage:add({
categoryMenu = source.categoryMenu
})
end
self.source = source
self.categoryMenu = source.categoryMenu
categoryPage:setCategory(source, source.name, source.index)
end
function categoryPage.grid:sortCompare(a, b)
return a.ltitle < b.ltitle
end
function categoryPage.grid:getRowTextColor(row, selected)
if fs.exists(fs.combine('/apps', row.name)) then
return colors.orange
end
return UI.Grid:getRowTextColor(row, selected)
end
function categoryPage:eventHandler(event)
if event.type == 'grid_select' or event.type == 'select' then
UI:setPage(appPage, self.source, self.grid:getSelected())
elseif event.type == 'category' then
self:setCategory(self.source, event.button.text, event.button.index)
self:setFocus(self.grid)
self:draw()
elseif event.type == 'source' then
self:setFocus(self.grid)
self:setSource(event.button)
self:draw()
elseif event.type == 'quit' then
Event.exitPullEvents()
else
return UI.Page.eventHandler(self, event)
end
return true
end
print("Retrieving catalog list")
categoryPage:setSource(sources[1])
UI:setPage(categoryPage)
Event.pullEvents()
UI.term:reset()

118
sys/apps/Events.lua Normal file
View File

@@ -0,0 +1,118 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
multishell.setTitle(multishell.getCurrent(), 'Events')
UI:configure('Events', ...)
local page = UI.Page({
menuBar = UI.MenuBar({
buttons = {
{ text = 'Filter', event = 'filter' },
{ text = 'Reset', event = 'reset' },
{ text = 'Pause ', event = 'toggle', name = 'pauseButton' },
},
}),
grid = UI.Grid({
y = 2,
columns = {
{ heading = 'Event', key = 'event' },
{ key = 'p1' },
{ key = 'p2' },
{ key = 'p3' },
{ key = 'p4' },
{ key = 'p5' },
},
autospace = true,
}),
accelerators = {
f = 'filter',
p = 'toggle',
r = 'reset',
c = 'clear',
q = 'quit',
},
filtered = { },
})
function page:eventHandler(event)
if event.type == 'filter' then
local entry = self.grid:getSelected()
self.filtered[entry.event] = true
elseif event.type == 'toggle' then
self.paused = not self.paused
if self.paused then
self.menuBar.pauseButton.text = 'Resume'
else
self.menuBar.pauseButton.text = 'Pause '
end
self.menuBar:draw()
elseif event.type == 'reset' then
self.filtered = { }
self.grid:setValues({ })
self.grid:draw()
if self.paused then
self:emit({ type = 'toggle' })
end
elseif event.type == 'clear' then
self.grid:setValues({ })
self.grid:draw()
elseif event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'focus_change' then
if event.focused == self.grid then
if not self.paused then
self:emit({ type = 'toggle' })
end
end
else
return UI.Page.eventHandler(self, event)
end
return true
end
function page.grid:draw()
self:adjustWidth()
UI.Grid.draw(self)
end
function eventLoop()
local function tovalue(s)
if type(s) == 'table' then
return 'table'
end
return s
end
while true do
local e = { os.pullEvent() }
if not page.paused and not page.filtered[e[1]] then
table.insert(page.grid.values, 1, {
event = e[1],
p1 = tovalue(e[2]),
p2 = tovalue(e[3]),
p3 = tovalue(e[4]),
p4 = tovalue(e[5]),
p5 = tovalue(e[6]),
})
if #page.grid.values > page.grid.height - 1 then
table.remove(page.grid.values, #page.grid.values)
end
page.grid:update()
page.grid:draw()
page:sync()
end
end
end
UI:setPage(page)
Event.pullEvents(eventLoop)
UI.term:reset()

414
sys/apps/Files.lua Normal file
View File

@@ -0,0 +1,414 @@
require = requireInjector(getfenv(1))
local Util = require('util')
local Event = require('event')
local UI = require('ui')
local Config = require('config')
local cleanEnv = Util.shallowCopy(getfenv(1))
cleanEnv.require = nil
multishell.setTitle(multishell.getCurrent(), 'Files')
UI:configure('Files', ...)
local config = {
showHidden = false,
showDirSizes = false,
}
Config.load('Files', config)
local copied = { }
local marked = { }
local directories = { }
local cutMode = false
function formatSize(size)
if size >= 1000000 then
return string.format('%dM', math.floor(size/1000000, 2))
elseif size >= 1000 then
return string.format('%dK', math.floor(size/1000, 2))
end
return size
end
local Browser = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = '^-', event = 'updir' },
{ text = 'File', event = 'dropdown', dropdown = 'fileMenu' },
{ text = 'Edit', event = 'dropdown', dropdown = 'editMenu' },
{ text = 'View', event = 'dropdown', dropdown = 'viewMenu' },
},
},
fileMenu = UI.DropMenu {
buttons = {
{ text = 'Run', event = 'run' },
{ text = 'Edit e', event = 'edit' },
{ text = 'Shell s', event = 'shell' },
UI.Text { value = ' ------------ ' },
{ text = 'Quit q', event = 'quit' },
UI.Text { },
}
},
editMenu = UI.DropMenu {
buttons = {
{ text = 'Cut ^x', event = 'cut' },
{ text = 'Copy ^c', event = 'copy' },
{ text = 'Paste ^v', event = 'paste' },
UI.Text { value = ' --------------- ' },
{ text = 'Mark m', event = 'mark' },
{ text = 'Unmark all u', event = 'unmark' },
UI.Text { value = ' --------------- ' },
{ text = 'Delete del', event = 'delete' },
UI.Text { },
}
},
viewMenu = UI.DropMenu {
buttons = {
{ text = 'Refresh r', event = 'refresh' },
{ text = 'Hidden ^h', event = 'toggle_hidden' },
{ text = 'Dir Size ^s', event = 'toggle_dirSize' },
UI.Text { },
}
},
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width-11 },
{ key = 'flags', width = 2 },
{ heading = 'Size', key = 'fsize', width = 6 },
},
sortColumn = 'name',
y = 2,
height = UI.term.height-2,
},
statusBar = UI.StatusBar {
columns = {
{ '', 'status', UI.term.width - 8 },
--{ '', 'info', 10 },
{ 'Size: ', 'totalSize', 8 },
},
},
accelerators = {
q = 'quit',
e = 'edit',
s = 'shell',
r = 'refresh',
space = 'mark',
backspace = 'updir',
m = 'move',
u = 'unmark',
d = 'delete',
delete = 'delete',
[ 'control-h' ] = 'toggle_hidden',
[ 'control-x' ] = 'cut',
[ 'control-c' ] = 'copy',
paste = 'paste',
},
}
function Browser:enable()
UI.Page.enable(self)
self:setFocus(self.grid)
end
function Browser.grid:sortCompare(a, b)
if self.sortColumn == 'fsize' then
return a.size < b.size
elseif self.sortColumn == 'flags' then
return a.flags < b.flags
end
if a.isDir == b.isDir then
return a.name:lower() < b.name:lower()
end
return a.isDir
end
function Browser.grid:getRowTextColor(file, selected)
if file.marked then
return colors.green
end
if file.isDir then
return colors.cyan
end
if file.isReadOnly then
return colors.pink
end
return colors.white
end
function Browser.grid:getRowBackgroundColorX(file, selected)
if selected then
return colors.gray
end
return self.backgroundColor
end
function Browser.grid:eventHandler(event)
if event.type == 'copy' then -- let copy be handled by parent
return false
end
return UI.Grid.eventHandler(self, event)
end
function Browser.statusBar:draw()
if self.parent.dir then
local info = '#:' .. Util.size(self.parent.dir.files)
local numMarked = Util.size(marked)
if numMarked > 0 then
info = info .. ' M:' .. numMarked
end
self:setValue('info', info)
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
UI.StatusBar.draw(self)
end
end
function Browser:setStatus(status, ...)
self.statusBar:timedStatus(string.format(status, ...))
end
function Browser:unmarkAll()
for k,m in pairs(marked) do
m.marked = false
end
Util.clear(marked)
end
function Browser:getDirectory(directory)
local s, dir = pcall(function()
local dir = directories[directory]
if not dir then
dir = {
name = directory,
size = 0,
files = { },
totalSize = 0,
index = 1
}
directories[directory] = dir
end
self:updateDirectory(dir)
return dir
end)
return s, dir
end
function Browser:updateDirectory(dir)
dir.size = 0
dir.totalSize = 0
Util.clear(dir.files)
local files = fs.list(dir.name, true)
if files then
dir.size = #files
for _, file in pairs(files) do
file.fullName = fs.combine(dir.name, file.name)
file.directory = directory
file.flags = ''
if not file.isDir then
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
else
if config.showDirSizes then
file.size = fs.getSize(file.fullName, true)
dir.totalSize = dir.totalSize + file.size
file.fsize = formatSize(file.size)
end
file.flags = 'D'
end
if file.isReadOnly then
file.flags = file.flags .. 'R'
end
if config.showHidden or file.name:sub(1, 1) ~= '.' then
dir.files[file.fullName] = file
end
end
end
-- self.grid:update()
-- self.grid:setIndex(dir.index)
self.grid:setValues(dir.files)
end
function Browser:setDir(dirName, noStatus)
self:unmarkAll()
if self.dir then
self.dir.index = self.grid:getIndex()
end
DIR = fs.combine('', dirName)
shell.setDir(DIR)
local s, dir = self:getDirectory(DIR)
if s then
self.dir = dir
elseif noStatus then
error(dir)
else
self:setStatus(dir)
self:setDir('', true)
return
end
if not noStatus then
self.statusBar:setValue('status', '/' .. self.dir.name)
self.statusBar:draw()
end
self.grid:setIndex(self.dir.index)
end
function Browser:run(path, ...)
local tabId = multishell.launch(cleanEnv, path, ...)
multishell.setFocus(tabId)
end
function Browser:hasMarked()
if Util.size(marked) == 0 then
local file = self.grid:getSelected()
if file then
file.marked = true
marked[file.fullName] = file
self.grid:draw()
end
end
return Util.size(marked) > 0
end
function Browser:eventHandler(event)
local file = self.grid:getSelected()
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'edit' and file then
self:run('/apps/shell', 'edit', file.name)
elseif event.type == 'shell' then
self:run('/apps/shell')
elseif event.type == 'refresh' then
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Refreshed')
elseif event.type == 'toggle_hidden' then
config.showHidden = not config.showHidden
Config.update('Files', config)
self:updateDirectory(self.dir)
self.grid:draw()
if not config.showHidden then
self:setStatus('Hiding hidden')
else
self:setStatus('Displaying hidden')
end
elseif event.type == 'toggle_dirSize' then
config.showDirSizes = not config.showDirSizes
Config.update('Files', config)
self:updateDirectory(self.dir)
self.grid:draw()
if config.showDirSizes then
self:setStatus('Displaying dir sizes')
end
elseif event.type == 'mark' and file then
file.marked = not file.marked
if file.marked then
marked[file.fullName] = file
else
marked[file.fullName] = nil
end
self.grid:draw()
self.statusBar:draw()
elseif event.type == 'unmark' then
self:unmarkAll()
self.grid:draw()
self:setStatus('Marked files cleared')
elseif event.type == 'grid_select' or event.type == 'run' then
if file then
if file.isDir then
self:setDir(file.fullName)
else
self:run('/apps/shell', file.name)
end
end
elseif event.type == 'updir' then
local dir = (self.dir.name:match("(.*/)"))
self:setDir(dir or '/')
elseif event.type == 'delete' then
if self:hasMarked() then
local width = self.statusBar:getColumnWidth('status')
self.statusBar:setColumnWidth('status', UI.term.width)
self.statusBar:setValue('status', 'Delete marked? (y/n)')
self.statusBar:draw()
self.statusBar:sync()
local _, ch = os.pullEvent('char')
if ch == 'y' or ch == 'Y' then
for k,m in pairs(marked) do
pcall(function()
fs.delete(m.fullName)
end)
end
end
marked = { }
self.statusBar:setColumnWidth('status', width)
self.statusBar:setValue('status', '/' .. self.dir.name)
self:updateDirectory(self.dir)
self.statusBar:draw()
self.grid:draw()
self:setFocus(self.grid)
end
elseif event.type == 'copy' or event.type == 'cut' then
if self:hasMarked() then
cutMode = event.type == 'cut'
Util.clear(copied)
Util.merge(copied, marked)
--self:unmarkAll()
self.grid:draw()
self:setStatus('Copied %d file(s)', Util.size(copied))
end
elseif event.type == 'paste' then
for k,m in pairs(copied) do
local s, m = pcall(function()
if cutMode then
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
else
fs.copy(m.fullName, fs.combine(self.dir.name, m.name))
end
end)
end
self:updateDirectory(self.dir)
self.grid:draw()
self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)')
else
return UI.Page.eventHandler(self, event)
end
self:setFocus(self.grid)
return true
end
--[[-- Startup logic --]]--
local args = { ... }
Browser:setDir(args[1] or shell.dir())
UI:setPage(Browser)
Event.pullEvents()
UI.term:reset()

84
sys/apps/Help.lua Normal file
View File

@@ -0,0 +1,84 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
multishell.setTitle(multishell.getCurrent(), 'Help')
UI:configure('Help', ...)
local files = { }
for _,f in pairs(fs.list('/rom/help')) do
table.insert(files, { name = f })
end
local page = UI.Page({
labelText = UI.Text({
y = 2,
x = 3,
value = 'Search',
}),
filter = UI.TextEntry({
y = 2,
x = 10,
width = UI.term.width - 13,
limit = 32,
}),
grid = UI.ScrollingGrid({
y = 4,
height = UI.term.height - 4,
values = files,
columns = {
{ heading = 'Name', key = 'name', width = 12 },
},
sortColumn = 'name',
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'quit',
},
})
local function showHelp(name)
UI.term:reset()
shell.run('help ' .. name)
print('Press enter to return')
read()
end
function page:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'key' and event.key == 'enter' then
showHelp(self.grid:getSelected().name)
self:setFocus(self.filter)
self:draw()
elseif event.type == 'grid_select' then
showHelp(event.selected.name)
self:setFocus(self.filter)
self:draw()
elseif event.type == 'text_change' then
local text = event.text
if #text == 0 then
self.grid.values = files
else
self.grid.values = { }
for _,f in pairs(files) do
if string.find(f.name, text) then
table.insert(self.grid.values, f)
end
end
end
self.grid:update()
self.grid:setIndex(1)
self.grid:draw()
else
UI.Page.eventHandler(self, event)
end
end
UI:setPage(page)
Event.pullEvents()
UI.term:reset()

314
sys/apps/Lua.lua Normal file
View File

@@ -0,0 +1,314 @@
local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local Util = require('util')
local UI = require('ui')
local Event = require('event')
local History = require('history')
local sandboxEnv = Util.shallowCopy(getfenv(1))
sandboxEnv.exit = function() Event.exitPullEvents() end
sandboxEnv.require = injector(sandboxEnv)
setmetatable(sandboxEnv, { __index = _G })
multishell.setTitle(multishell.getCurrent(), 'Lua')
UI:configure('Lua', ...)
local command = ''
local history = History.load('usr/.lua_history', 25)
local page = UI.Page({
menuBar = UI.MenuBar({
buttons = {
{ text = 'Local', event = 'local' },
{ text = 'Global', event = 'global' },
{ text = 'Device', event = 'device', name = 'Device' },
},
}),
prompt = UI.TextEntry({
y = 2,
shadowText = 'enter command',
backgroundFocusColor = colors.black,
limit = 256,
accelerators = {
enter = 'command_enter',
up = 'history_back',
down = 'history_forward',
mouse_rightclick = 'clear_prompt',
-- [ 'control-space' ] = 'autocomplete',
},
}),
grid = UI.ScrollingGrid({
y = 3,
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value' },
},
sortColumn = 'name',
autospace = true,
}),
notification = UI.Notification(),
})
function page:setPrompt(value, focus)
self.prompt:setValue(value)
self.prompt.scroll = 0
self.prompt:setPosition(#value)
self.prompt:updateScroll()
if value:sub(-1) == ')' then
self.prompt:setPosition(#value - 1)
end
self.prompt:draw()
if focus then
page:setFocus(self.prompt)
end
end
function page:enable()
self:setFocus(self.prompt)
UI.Page.enable(self)
end
local function autocomplete(env, oLine, x)
local sLine = oLine:sub(1, x)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
if #sLine > 0 then
local results = textutils.complete(sLine, env)
if #results == 0 then
-- setError('No completions available')
elseif #results == 1 then
return Util.insertString(oLine, results[1], x + 1)
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = results[n]
while #prefix > 0 do
if result:find(prefix, 1, true) == 1 then
break
end
prefix = prefix:sub(1, #prefix - 1)
end
end
if #prefix > 0 then
return Util.insertString(oLine, prefix, x + 1)
else
-- setStatus('Too many results')
end
end
end
return oLine
end
function page:eventHandler(event)
if event.type == 'global' then
self:setPrompt('', true)
self:executeStatement('getfenv(0)')
command = nil
elseif event.type == 'local' then
self:setPrompt('', true)
self:executeStatement('getfenv(1)')
command = nil
elseif event.type == 'autocomplete' then
local sz = #self.prompt.value
local pos = self.prompt.pos
self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.pos))
self.prompt:setPosition(pos + #self.prompt.value - sz)
self.prompt:updateCursor()
elseif event.type == 'device' then
if not _G.device then
sandboxEnv.device = { }
for _,side in pairs(peripheral.getNames()) do
local key = string.format('%s:%s', peripheral.getType(side), side)
sandboxEnv.device[ key ] = peripheral.wrap(side)
end
end
self:setPrompt('device', true)
self:executeStatement('device')
elseif event.type == 'history_back' then
local value = history.back()
if value then
self:setPrompt(value)
end
elseif event.type == 'history_forward' then
self:setPrompt(history.forward() or '')
elseif event.type == 'clear_prompt' then
self:setPrompt('')
history.setPosition(#history.entries + 1)
elseif event.type == 'command_enter' then
local s = tostring(self.prompt.value)
if #s > 0 then
history.add(s)
self:executeStatement(s)
else
local t = { }
for k = #history.entries, 1, -1 do
table.insert(t, {
name = #t + 1,
value = history.entries[k],
isHistory = true,
pos = k,
})
end
history.setPosition(#history.entries + 1)
command = nil
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
return true
else
return UI.Page.eventHandler(self, event)
end
return true
end
function page:setResult(result)
local t = { }
local function safeValue(v)
local t = type(v)
if t == 'string' or t == 'number' then
return v
end
return tostring(v)
end
if type(result) == 'table' then
for k,v in pairs(result) do
local entry = {
name = safeValue(k),
rawName = k,
value = safeValue(v),
rawValue = v,
}
if type(v) == 'table' then
if Util.size(v) == 0 then
entry.value = 'table: (empty)'
else
entry.value = 'table'
end
end
table.insert(t, entry)
end
else
table.insert(t, {
name = type(result),
value = tostring(result),
rawValue = result,
})
end
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
function page.grid:eventHandler(event)
local entry = self:getSelected()
local function commandAppend()
if entry.isHistory then
history.setPosition(entry.pos)
return entry.value
end
if type(entry.rawValue) == 'function' then
if command then
return command .. '.' .. entry.name .. '()'
end
return entry.name .. '()'
end
if command then
if type(entry.rawName) == 'number' then
return command .. '[' .. entry.name .. ']'
end
if entry.name:match("%W") or
entry.name:sub(1, 1):match("%d") then
return command .. "['" .. tostring(entry.name) .. "']"
end
return command .. '.' .. entry.name
end
return entry.name
end
if event.type == 'grid_focus_row' then
if self.focused then
page:setPrompt(commandAppend())
end
elseif event.type == 'grid_select' then
page:setPrompt(commandAppend(), true)
page:executeStatement(commandAppend())
elseif event.type == 'copy' then
if entry then
clipboard.setData(entry.rawValue)
end
else
return UI.Grid.eventHandler(self, event)
end
return true
end
function page:rawExecute(s)
local fn, m = loadstring("return (" .. s .. ')', 'lua')
if not fn then
fn, m = loadstring(s, 'lua')
end
if fn then
setfenv(fn, sandboxEnv)
fn, m = pcall(fn)
end
return fn, m
end
function page:executeStatement(statement)
command = statement
local s, m = self:rawExecute(command)
if s and m then
self:setResult(m)
elseif s and type(m) == 'boolean' then
self:setResult(m)
else
self.grid:setValues({ })
self.grid:draw()
if m then
self.notification:error(m, 5)
end
end
end
local args = { ... }
if args[1] then
command = 'args[1]'
sandboxEnv.args = args
page:setResult(args[1])
end
UI:setPage(page)
Event.pullEvents()
UI.term:reset()

148
sys/apps/Network.lua Normal file
View File

@@ -0,0 +1,148 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
local Socket = require('socket')
multishell.setTitle(multishell.getCurrent(), 'Network')
UI:configure('Network', ...)
local gridColumns = {
{ heading = 'Label', key = 'label' },
{ heading = 'Dist', key = 'distance' },
{ heading = 'Status', key = 'status' },
}
if UI.term.width >= 30 then
table.insert(gridColumns, { heading = 'Fuel', key = 'fuel' })
table.insert(gridColumns, { heading = 'Uptime', key = 'uptime' })
end
local page = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Telnet', event = 'telnet' },
{ text = 'VNC', event = 'vnc' },
{ text = 'Trust', event = 'trust' },
{ text = 'Reboot', event = 'reboot' },
},
},
grid = UI.ScrollingGrid {
y = 2,
values = network,
columns = gridColumns,
sortColumn = 'label',
autospace = true,
},
notification = UI.Notification { },
accelerators = {
q = 'quit',
c = 'clear',
},
}
local function sendCommand(host, command)
if not device.wireless_modem then
page.notification:error('Wireless modem not present')
return
end
page.notification:info('Connecting')
page:sync()
local socket = Socket.connect(host, 161)
if socket then
socket:write({ type = command })
socket:close()
page.notification:success('Command sent')
else
page.notification:error('Failed to connect')
end
end
function page:eventHandler(event)
local t = self.grid:getSelected()
if t then
if event.type == 'telnet' or event.type == 'grid_select' then
multishell.openTab({
path = '/apps/telnet.lua',
focused = true,
args = { t.id },
title = t.label,
})
elseif event.type == 'vnc' then
multishell.openTab({
path = '/apps/vnc.lua',
focused = true,
args = { t.id },
title = t.label,
})
elseif event.type == 'trust' then
shell.openForegroundTab('trust ' .. t.id)
elseif event.type == 'reboot' then
sendCommand(t.id, 'reboot')
elseif event.type == 'shutdown' then
sendCommand(t.id, 'shutdown')
end
end
if event.type == 'quit' then
Event.exitPullEvents()
end
UI.Page.eventHandler(self, event)
end
function page.grid:getRowTextColor(row, selected)
if not row.active then
return colors.orange
end
return UI.Grid.getRowTextColor(self, row, selected)
end
function page.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
if row.uptime then
if row.uptime < 60 then
row.uptime = string.format("%ds", math.floor(row.uptime))
else
row.uptime = string.format("%sm", math.floor(row.uptime/6)/10)
end
end
if row.fuel then
row.fuel = Util.toBytes(row.fuel)
end
if row.distance then
row.distance = Util.round(row.distance, 1)
end
return row
end
Event.addThread(function()
while true do
page.grid:update()
page.grid:draw()
page:sync()
os.sleep(1)
end
end)
Event.addHandler('device_attach', function(h, deviceName)
if deviceName == 'wireless_modem' then
page.notification:success('Modem connected')
page:sync()
end
end)
Event.addHandler('device_detach', function(h, deviceName)
if deviceName == 'wireless_modem' then
page.notification:error('Wireless modem not attached')
page:sync()
end
end)
if not device.wireless_modem then
page.notification:error('Wireless modem not attached')
end
UI:setPage(page)
Event.pullEvents()
UI.term:reset()

478
sys/apps/Overview.lua Normal file
View File

@@ -0,0 +1,478 @@
require = requireInjector(getfenv(1))
local Util = require('util')
local Event = require('event')
local UI = require('ui')
local Config = require('config')
local NFT = require('nft')
local class = require('class')
local FileUI = require('fileui')
local Tween = require('tween')
multishell.setTitle(multishell.getCurrent(), 'Overview')
UI:configure('Overview', ...)
local config = {
Recent = { },
currentCategory = 'Apps',
}
local applications = { }
Config.load('Overview', config)
local function loadApplications()
Util.clear(applications)
local apps = fs.list('sys/apps/.overview')
for _,app in pairs(apps) do
local data = Util.readTable('sys/apps/.overview/' .. app)
if data then
data.filename = 'sys/apps/.overview/' .. app
table.insert(applications, data)
end
end
end
loadApplications()
local defaultIcon = NFT.parse([[
8071180
8007180
7180071]])
local sx, sy = term.current().getSize()
local maxRecent = math.ceil(sx * sy / 62)
local function elipse(s, len)
if #s > len then
s = s:sub(1, len - 2) .. '..'
end
return s
end
local buttons = { }
local categories = { }
table.insert(buttons, { text = 'Recent', event = 'category' })
for _,f in pairs(applications) do
if not categories[f.category] then
categories[f.category] = true
table.insert(buttons, { text = f.category, event = 'category' })
end
end
table.insert(buttons, { text = '+', event = 'new' })
local function parseIcon(iconText)
local icon
local s, m = pcall(function()
icon = NFT.parse(iconText)
if icon then
if icon.height > 3 or icon.width > 8 then
error('Invalid size')
end
end
return icon
end)
if s then
return icon
end
return s, m
end
local page = UI.Page {
tabBar = UI.TabBar {
buttons = buttons,
},
container = UI.ViewportWindow {
y = 2,
},
notification = UI.Notification(),
accelerators = {
r = 'refresh',
e = 'edit',
s = 'shell',
l = 'lua',
[ 'control-l' ] = 'refresh',
[ 'control-n' ] = 'new',
delete = 'delete',
},
}
function page:draw()
self.tabBar:draw()
self.container:draw()
end
UI.Icon = class(UI.Window)
function UI.Icon:init(args)
local defaults = {
UIElement = 'Icon',
width = 14,
height = 4,
}
UI.setProperties(defaults, args)
UI.Window.init(self, defaults)
end
function UI.Icon:eventHandler(event)
if event.type == 'mouse_click' then
self:setFocus(self.button)
return true
elseif event.type == 'mouse_doubleclick' then
self:emit({ type = self.button.event, button = self.button })
elseif event.type == 'mouse_rightclick' then
self:setFocus(self.button)
self:emit({ type = 'edit', button = self.button })
end
return UI.Window.eventHandler(self, event)
end
function page.container:setCategory(categoryName)
-- reset the viewport window
self.children = { }
self.offy = 0
local function filter(it, f)
local ot = { }
for _,v in pairs(it) do
if f(v) then
table.insert(ot, v)
end
end
return ot
end
local filtered
if categoryName == 'Recent' then
filtered = { }
for _,v in ipairs(config.Recent) do
local app = Util.find(applications, 'run', v)
if app and fs.exists(app.run) then
table.insert(filtered, app)
end
end
else
filtered = filter(applications, function(a)
return a.category == categoryName -- and fs.exists(a.run)
end)
table.sort(filtered, function(a, b) return a.title < b.title end)
end
for _,program in ipairs(filtered) do
local icon
if program.icon then
icon = parseIcon(program.icon)
end
if not icon then
icon = defaultIcon
end
local title = elipse(program.title, 8)
local width = math.max(icon.width + 2, #title + 2)
table.insert(self.children, UI.Icon({
width = width,
image = UI.NftImage({
x = math.floor((width - icon.width) / 2) + 1,
image = icon,
width = 5,
height = 3,
}),
button = UI.Button({
x = math.floor((width - #title - 2) / 2) + 1,
y = 4,
text = title,
backgroundColor = self.backgroundColor,
width = #title + 2,
event = 'button',
app = program,
}),
}))
end
local gutter = 2
if UI.term.width <= 26 then
gutter = 1
end
local col, row = gutter, 2
local count = #self.children
-- reposition all children
for k,child in ipairs(self.children) do
child.x = -10
child.y = math.floor(self.height)
child.tween = Tween.new(6, child, { x = col, y = row }, 'outSine')
if k < count then
col = col + child.width
if col + self.children[k + 1].width + gutter - 2 > UI.term.width then
col = gutter
row = row + 5
end
end
end
self:initChildren()
self.animate = true
end
function page.container:draw()
if self.animate then
self.animate = false
for i = 1, 6 do
for _,child in ipairs(self.children) do
child.tween:update(1)
child.x = math.floor(child.x)
child.y = math.floor(child.y)
end
UI.ViewportWindow.draw(self)
self:sync()
os.sleep()
end
else
UI.ViewportWindow.draw(self)
end
end
function page:refresh()
local pos = self.container.offy
self:focusFirst(self)
self.container:setCategory(config.currentCategory)
self.container:setScrollPosition(pos)
end
function page:resize()
self:refresh()
UI.Page.resize(self)
end
function page:eventHandler(event)
if event.type == 'category' then
self.tabBar:selectTab(event.button.text)
self.container:setCategory(event.button.text)
self.container:draw()
self:sync()
config.currentCategory = event.button.text
Config.update('Overview', config)
elseif event.type == 'button' then
for k,v in ipairs(config.Recent) do
if v == event.button.app.run then
table.remove(config.Recent, k)
break
end
end
table.insert(config.Recent, 1, event.button.app.run)
if #config.Recent > maxRecent then
table.remove(config.Recent, maxRecent + 1)
end
Config.update('Overview', config)
multishell.openTab({
path = 'sys/apps/shell',
args = { event.button.app.run },
focused = true,
})
elseif event.type == 'shell' then
multishell.openTab({
path = 'sys/apps/shell',
focused = true,
})
elseif event.type == 'lua' then
multishell.openTab({
path = 'sys/apps/Lua.lua',
focused = true,
})
elseif event.type == 'focus_change' then
if event.focused.parent.UIElement == 'Icon' then
event.focused.parent:scrollIntoView()
end
elseif event.type == 'tab_change' then
if event.current > event.last then
--self.container:setTransition(UI.effect.slideLeft)
else
--self.container:setTransition(UI.effect.slideRight)
end
elseif event.type == 'refresh' then
loadApplications()
self:refresh()
self:draw()
self.notification:success('Refreshed')
elseif event.type == 'delete' then
local focused = page:getFocused()
if focused.app then
fs.delete(focused.app.filename)
loadApplications()
page:refresh()
page:draw()
self.notification:success('Removed')
end
elseif event.type == 'new' then
local category = 'Apps'
if config.currentCategory ~= 'Recent' then
category = config.currentCategory or 'Apps'
end
UI:setPage('editor', { category = category })
elseif event.type == 'edit' then
local focused = page:getFocused()
if focused.app then
UI:setPage('editor', focused.app)
end
else
UI.Page.eventHandler(self, event)
end
return true
end
local formWidth = math.max(UI.term.width - 14, 26)
local editor = UI.Dialog {
height = 11,
width = formWidth,
title = 'Edit application',
form = UI.Form {
y = 2,
height = 9,
title = UI.TextEntry {
formLabel = 'Title', formKey = 'title', limit = 11, help = 'Application title',
required = true,
},
run = UI.TextEntry {
formLabel = 'Run', formKey = 'run', limit = 100, help = 'Full path to application',
required = true,
},
category = UI.TextEntry {
formLabel = 'Category', formKey = 'category', limit = 11, help = 'Category of application',
required = true,
},
loadIcon = UI.Button {
x = 11, y = 6,
text = 'Icon', event = 'loadIcon', help = 'Select icon'
},
image = UI.NftImage {
y = 6,
x = 2,
height = 3,
width = 8,
},
},
statusBar = UI.StatusBar(),
iconFile = '',
}
function editor:enable(app)
if app then
self.form:setValues(app)
local icon
if app.icon then
icon = parseIcon(app.icon)
end
self.form.image:setImage(icon)
end
UI.Page.enable(self)
self:focusFirst()
end
function editor.form.image:draw()
self:clear()
UI.NftImage.draw(self)
end
function editor:updateApplications(app)
if not app.filename then
app.filename = 'sys/apps/.overview/' .. app.title
end
Util.writeTable(app.filename, app)
loadApplications()
end
function editor:eventHandler(event)
if event.type == 'form_cancel' or event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help or '')
self.statusBar:draw()
elseif event.type == 'loadIcon' then
local fileui = FileUI({
x = self.x,
y = self.y,
z = 2,
width = self.width,
height = self.height,
})
--fileui:setTransition(UI.effect.explode)
UI:setPage(fileui, fs.getDir(self.iconFile), function(fileName)
if fileName then
self.iconFile = fileName
local s, m = pcall(function()
local iconLines = Util.readFile(fileName)
if not iconLines then
error('Unable to load file')
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
self.form.values.icon = iconLines
self.form.image:setImage(icon)
self.form.image:draw()
end)
if not s and m then
local msg = m:gsub('.*: (.*)', '%1')
page.notification:error(msg)
end
end
end)
elseif event.type == 'form_invalid' then
page.notification:error(event.message)
elseif event.type == 'form_complete' then
local values = self.form.values
UI:setPreviousPage()
self:updateApplications(values)
page:refresh()
page:draw()
else
return UI.Page.eventHandler(self, event)
end
return true
end
UI:setPages({
editor = editor,
main = page,
})
Event.addHandler('os_register_app', function()
loadApplications()
page:refresh()
page:draw()
page:sync()
end)
page.tabBar:selectTab(config.currentCategory or 'Apps')
page.container:setCategory(config.currentCategory or 'Apps')
UI:setPage(page)
Event.pullEvents()
UI.term:reset()

219
sys/apps/Peripherals.lua Normal file
View File

@@ -0,0 +1,219 @@
local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local Util = require('util')
local Event = require('event')
local UI = require('ui')
multishell.setTitle(multishell.getCurrent(), 'Devices')
--[[ -- PeripheralsPage -- ]] --
local peripheralsPage = UI.Page {
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Type', key = 'type' },
{ heading = 'Side', key = 'side' },
},
sortColumn = 'type',
height = UI.term.height - 1,
autospace = true,
},
statusBar = UI.StatusBar {
status = 'Select peripheral'
},
accelerators = {
q = 'quit',
},
}
function peripheralsPage.grid:draw()
local sides = peripheral.getNames()
Util.clear(self.values)
for _,side in pairs(sides) do
table.insert(self.values, {
type = peripheral.getType(side),
side = side
})
end
self:update()
self:adjustWidth()
UI.Grid.draw(self)
end
function peripheralsPage:updatePeripherals()
if UI:getCurrentPage() == self then
self.grid:draw()
self:sync()
end
end
function peripheralsPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'grid_select' then
UI:setPage('methods', event.selected)
end
return UI.Page.eventHandler(self, event)
end
--[[ -- MethodsPage -- ]] --
local methodsPage = UI.Page {
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width }
},
sortColumn = 'name',
height = 7,
},
viewportConsole = UI.ViewportWindow {
y = 8,
height = UI.term.height - 8,
backgroundColor = colors.brown,
},
statusBar = UI.StatusBar {
status = 'q to return',
},
accelerators = {
q = 'back',
backspace = 'back',
},
}
function methodsPage:enable(p)
self.peripheral = p or self.peripheral
local p = peripheral.wrap(self.peripheral.side)
if p.getDocs then
self.grid.values = { }
for k,v in pairs(p.getDocs()) do
table.insert(self.grid.values, {
name = k,
doc = v,
})
end
elseif not p.getAdvancedMethodsData then
self.grid.values = { }
for name,f in pairs(p) do
table.insert(self.grid.values, {
name = name,
noext = true,
})
end
else
self.grid.values = p.getAdvancedMethodsData()
for name,f in pairs(self.grid.values) do
f.name = name
end
end
self.viewportConsole.offy = 0
self.grid:update()
self.grid:setIndex(1)
self.statusBar:setStatus(self.peripheral.type)
UI.Page.enable(self)
end
function methodsPage:eventHandler(event)
if event.type == 'back' then
UI:setPage(peripheralsPage)
return true
elseif event.type == 'grid_focus_row' then
self.viewportConsole.offy = 0
self.viewportConsole:draw()
end
return UI.Page.eventHandler(self, event)
end
function methodsPage.viewportConsole:draw()
local c = self
local method = methodsPage.grid:getSelected()
c:clear()
c:setCursorPos(1, 1)
if method.noext then
c.cursorY = 2
c:print('No extended Information')
return 2
end
if method.doc then
c:print(method.doc, nil, colors.yellow)
c.ymax = c.cursorY + 1
return
end
if method.description then
c:print(method.description)
end
c.cursorY = c.cursorY + 2
c.cursorX = 1
if method.returnTypes ~= '()' then
c:print(method.returnTypes .. ' ', nil, colors.yellow)
end
c:print(method.name, nil, colors.black)
c:print('(')
local maxArgLen = 1
for k,arg in ipairs(method.args) do
if #arg.description > 0 then
maxArgLen = math.max(#arg.name, maxArgLen)
end
local argName = arg.name
local fg = colors.green
if arg.optional then
argName = string.format('[%s]', arg.name)
fg = colors.orange
end
c:print(argName, nil, fg)
if k < #method.args then
c:print(', ')
end
end
c:print(')')
c.cursorY = c.cursorY + 1
if #method.args > 0 then
for _,arg in ipairs(method.args) do
if #arg.description > 0 then
c.cursorY = c.cursorY + 1
c.cursorX = 1
local fg = colors.green
if arg.optional then
fg = colors.orange
end
c:print(arg.name .. ': ', nil, fg)
c.cursorX = maxArgLen + 3
c:print(arg.description, nil, nil, maxArgLen + 3)
end
end
end
c.ymax = c.cursorY + 1
end
Event.addHandler('peripheral', function()
peripheralsPage:updatePeripherals()
end)
Event.addHandler('peripheral_detach', function()
peripheralsPage:updatePeripherals()
end)
UI:setPage(peripheralsPage)
UI:setPages({
methods = methodsPage,
})
Event.pullEvents()
UI.term:reset()

105
sys/apps/Pim.lua Normal file
View File

@@ -0,0 +1,105 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
local Config = require('config')
multishell.setTitle(multishell.getCurrent(), 'PIM')
local inventory = { }
local mode = 'sync'
if not device.pim then
error('PIM not attached')
end
local page = UI.Page({
menu = UI.Menu({
centered = true,
y = 2,
menuItems = {
{ prompt = 'Learn', event = 'learn', help = '' },
},
}),
statusBar = UI.StatusBar({
columns = {
{ 'Status', 'status', UI.term.width - 7 },
{ 'Mode', 'mode', 7 }
}
}),
accelerators = {
q = 'quit',
},
})
local function learn()
if device.pim.getInventorySize() > 0 then
local stacks = device.pim.getAllStacks(false)
Config.update('pim', stacks)
mode = 'sync'
page.statusBar:setValue('status', 'Learned inventory')
end
page.statusBar:setValue('mode', mode)
page.statusBar:draw()
end
function page:eventHandler(event)
if event.type == 'learn' then
mode = 'learn'
learn()
elseif event.type == 'quit' then
Event.exitPullEvents()
end
return UI.Page.eventHandler(self, event)
end
local function inInventory(s)
for _,i in pairs(inventory) do
if i.id == s.id then
return true
end
end
end
local function pimWatcher()
local playerOn = false
while true do
if device.pim.getInventorySize() > 0 and not playerOn then
playerOn = true
if mode == 'learn' then
learn()
else
local stacks = device.pim.getAllStacks(false)
for k,stack in pairs(stacks) do
if not inInventory(stack) then
device.pim.pushItem('down', k, stack.qty)
end
end
page.statusBar:setValue('status', 'Synchronized')
page.statusBar:draw()
end
elseif device.pim.getInventorySize() == 0 and playerOn then
page.statusBar:setValue('status', 'No player')
page.statusBar:draw()
playerOn = false
end
os.sleep(1)
end
end
Config.load('pim', inventory)
if Util.empty(inventory) then
mode = 'learn'
end
page.statusBar:setValue('mode', mode)
UI:setPage(page)
Event.pullEvents(pimWatcher)
UI.term:reset()

565
sys/apps/Script.lua Normal file
View File

@@ -0,0 +1,565 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
local Socket = require('socket')
local Config = require('config')
local GROUPS_PATH = 'usr/groups'
local SCRIPTS_PATH = 'usr/scripts'
multishell.setTitle(multishell.getCurrent(), 'Script')
UI:configure('script', ...)
local config = {
showGroups = false,
variables = [[{
COMPUTER_ID = os.getComputerID(),
}]],
}
Config.load('script', config)
local width = math.floor(UI.term.width / 2) - 1
if UI.term.width % 2 ~= 0 then
width = width + 1
end
function processVariables(script)
local fn = loadstring('return ' .. config.variables)
if fn then
local variables = fn()
for k,v in pairs(variables) do
local token = string.format('{%s}', k)
script = script:gsub(token, v)
end
end
return script
end
function invokeScript(computer, scriptName)
local script = Util.readFile(scriptName)
if not script then
print('Unable to read script file')
end
local socket = Socket.connect(computer.id, 161)
if not socket then
print('Unable to connect to ' .. computer.id)
return
end
script = processVariables(script)
Util.print('Running %s on %s', scriptName, computer.label)
socket:write({ type = 'script', args = script })
--[[
local response = socket:read(2)
if response and response.result then
if type(response.result) == 'table' then
print(textutils.serialize(response.result))
else
print(tostring(response.result))
end
else
printError('No response')
end
--]]
socket:close()
end
function runScript(computerOrGroup, scriptName)
if computerOrGroup.id then
invokeScript(computerOrGroup, scriptName)
else
local list = computerOrGroup.list
if computerOrGroup.path then
list = Util.readTable(computerOrGroup.path)
end
if list then
for _,computer in pairs(list) do
invokeScript(computer, scriptName)
end
end
end
end
local function getActiveComputers(t)
t = t or { }
Util.clear(t)
for k,computer in pairs(_G.network) do
if computer.active then
t[k] = computer
end
end
return t
end
local function getTurtleList()
local turtles = {
label = 'Turtles',
list = { },
}
for k,computer in pairs(getActiveComputers()) do
if computer.fuel then
turtles.list[k] = computer
end
end
return turtles
end
local args = { ... }
if #args == 2 then
local key = args[1]
local script = args[2]
local target
if tonumber(key) then
target = _G.network[tonumber(key)]
elseif key == 'All' then
target = {
list = Util.shallowCopy(getActiveComputers()),
}
elseif key == 'Localhost' then
target = { id = os.getComputerID() }
elseif key == 'Turtles' then
target = getTurtleList()
else
target = Util.readTable(fs.combine(GROUPS_PATH, key))
end
if not target then
error('Syntax: Script <ID or group> <script>')
end
runScript(target, fs.combine(SCRIPTS_PATH, script))
return
end
local function getListing(t, path)
Util.clear(t)
local files = fs.list(path)
for _,f in pairs(files) do
table.insert(t, { label = f, path = fs.combine(path, f) })
end
end
local mainPage = UI.Page({
menuBar = UI.MenuBar({
buttons = {
{ text = 'Groups', event = 'groups' },
{ text = 'Scripts', event = 'scripts' },
{ text = 'Toggle', event = 'toggle' },
},
}),
computers = UI.ScrollingGrid({
y = 2,
height = UI.term.height-3,
columns = {
{ heading = 'Label', key = 'label', width = width },
},
width = width,
sortColumn = 'label',
}),
scripts = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'label', width = width },
},
sortColumn = 'label',
height = UI.term.height - 3,
width = width,
x = UI.term.width - width + 1,
y = 2,
}),
statusBar = UI.StatusBar({
columns = {
{ '', 'status', 4 },
{ '', 'fuelF', 5 },
{ '', 'distanceF', 4 },
},
autospace = true,
}),
accelerators = {
q = 'quit',
},
})
local editorPage = UI.Page({
menuBar = UI.MenuBar({
showBackButton = true,
buttons = {
{ text = 'Save', event = 'save', help = 'Save this group' },
},
}),
grid1 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'label', width = width },
},
sortColumn = 'label',
height = UI.term.height - 4,
width = width,
y = 3,
}),
right = UI.Button({
text = '>',
event = 'right',
x = width - 2,
y = 2,
width = 3,
}),
left = UI.Button({
text = '<',
event = 'left',
x = UI.term.width - width + 1,
y = 2,
width = 3,
}),
grid2 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'label', width = width },
},
sortColumn = 'label',
height = UI.term.height - 4,
width = width,
x = UI.term.width - width + 1,
y = 3,
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'back',
},
})
local groupsPage = UI.Page({
menuBar = UI.MenuBar({
showBackButton = true,
buttons = {
{ text = 'Add', event = 'add' },
{ text = 'Edit', event = 'edit' },
{ text = 'Delete', event = 'delete' },
},
}),
grid = UI.ScrollingGrid({
y = 2,
height = UI.term.height-2,
columns = {
{ heading = 'Name', key = 'label' },
},
sortColumn = 'label',
autospace = true,
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'back',
},
})
local scriptsPage = UI.Page({
menuBar = UI.MenuBar({
showBackButton = true,
buttons = {
{ text = 'Add', event = 'add' },
{ text = 'Edit', event = 'edit' },
{ text = 'Delete', event = 'delete' },
},
}),
grid = UI.ScrollingGrid({
y = 2,
height = UI.term.height-2,
columns = {
{ heading = 'Name', key = 'label' },
},
sortColumn = 'label',
autospace = true,
}),
statusBar = UI.StatusBar(),
accelerators = {
a = 'add',
e = 'edit',
delete = 'delete',
q = 'back',
},
})
function editorPage:enable()
self:focusFirst()
local groupPath = fs.combine(GROUPS_PATH, self.groupName)
if fs.exists(groupPath) then
self.grid1.values = Util.readTable(groupPath)
else
Util.clear(self.grid1.values)
end
self.grid1:update()
UI.Page.enable(self)
end
function editorPage.grid2:draw()
getActiveComputers(self.values)
for k in pairs(editorPage.grid1.values) do
self.values[k] = nil
end
self:update()
UI.ScrollingGrid.draw(self)
end
function editorPage:eventHandler(event)
if event.type == 'back' then
UI:setPage(groupsPage)
elseif event.type == 'left' then
local computer = self.grid2:getSelected()
self.grid1.values[computer.id] = computer
self.grid1:update()
self.grid1:draw()
self.grid2:draw()
elseif event.type == 'right' then
local computer = self.grid1:getSelected()
self.grid1.values[computer.id] = nil
self.grid1:update()
self.grid1:draw()
self.grid2:draw()
elseif event.type == 'save' then
Util.writeTable(fs.combine(GROUPS_PATH, self.groupName), self.grid1.values)
UI:setPage(groupsPage)
end
return UI.Page.eventHandler(self, event)
end
local function nameDialog(f)
local dialog = UI.Dialog({
-- x = (UI.term.width - 28) / 2,
width = 22,
title = 'Enter Name',
form = UI.Form {
x = 2, rex = -2, y = 2,
textEntry = UI.TextEntry({ y = 3, width = 20, limit = 20 })
},
})
dialog.eventHandler = function(self, event)
if event.type == 'form_complete' then
local name = self.form.textEntry.value
if name then
f(name)
else
self.statusBar:timedStatus('Invalid Name', 3)
end
return true
elseif event.type == 'form_cancel' or event.type == 'cancel' then
UI:setPreviousPage()
else
return UI.Dialog.eventHandler(self, event)
end
end
dialog:setFocus(dialog.form.textEntry)
UI:setPage(dialog)
end
function groupsPage:draw()
getListing(self.grid.values, GROUPS_PATH)
self.grid:update()
UI.Page.draw(self)
end
function groupsPage:enable()
self:focusFirst()
UI.Page.enable(self)
end
function groupsPage:eventHandler(event)
if event.type == 'back' then
UI:setPage(mainPage)
elseif event.type == 'add' then
nameDialog(function(name)
editorPage.groupName = name
UI:setPage(editorPage)
end)
elseif event.type == 'delete' then
fs.delete(fs.combine(GROUPS_PATH, self.grid:getSelected().label))
self:draw()
elseif event.type == 'edit' then
editorPage.groupName = self.grid:getSelected().label
UI:setPage(editorPage)
end
return UI.Page.eventHandler(self, event)
end
function scriptsPage:draw()
getListing(self.grid.values, SCRIPTS_PATH)
self.grid:update()
UI.Page.draw(self)
end
function scriptsPage:enable()
self:focusFirst()
UI.Page.enable(self)
end
function scriptsPage:eventHandler(event)
if event.type == 'back' then
UI:setPreviousPage()
elseif event.type == 'add' then
nameDialog(function(name)
shell.run('edit ' .. fs.combine(SCRIPTS_PATH, name))
UI:setPreviousPage()
end)
elseif event.type == 'edit' then
local name = fs.combine(SCRIPTS_PATH, self.grid:getSelected().label)
shell.run('edit ' .. name)
self:draw()
elseif event.type == 'delete' then
local name = fs.combine(SCRIPTS_PATH, self.grid:getSelected().label)
fs.delete(name)
self:draw()
end
return UI.Page.eventHandler(self, event)
end
function mainPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'groups' then
UI:setPage(groupsPage)
elseif event.type == 'scripts' then
UI:setPage(scriptsPage)
elseif event.type == 'toggle' then
config.showGroups = not config.showGroups
local text = 'Computers'
if config.showGroups then
text = 'Groups'
end
-- self.statusBar.toggleButton.text = text
self:draw()
Config.update('script', config)
elseif event.type == 'grid_focus_row' then
local computer = self.computers:getSelected()
self.statusBar.values = { computer }
self.statusBar:draw()
elseif event.type == 'grid_select' then
local script = self.scripts:getSelected()
local computer = self.computers:getSelected()
self:clear()
self:sync()
self.enabled = false
runScript(computer, script.path)
print()
print('Press any key to continue...')
while true do
local e = os.pullEvent()
if e == 'char' or e == 'key' or e == 'mouse_click' then
break
end
end
self.enabled = true
self:draw()
end
return UI.Page.eventHandler(self, event)
end
function mainPage.statusBar:draw()
local computer = self.values[1]
if computer then
if computer.fuel then
computer.fuelF = string.format("%dk", math.floor(computer.fuel/1000))
end
if computer.distance then
computer.distanceF = Util.round(computer.distance, 1)
end
mainPage.statusBar:adjustWidth()
end
UI.StatusBar.draw(self)
end
function mainPage:draw()
getListing(self.scripts.values, SCRIPTS_PATH)
if config.showGroups then
getListing(self.computers.values, GROUPS_PATH)
table.insert(self.computers.values, {
label = 'All',
list = getActiveComputers(),
})
table.insert(self.computers.values, getTurtleList())
table.insert(self.computers.values, {
label = 'Localhost',
id = os.getComputerID(),
})
else
getActiveComputers(self.computers.values)
end
self.scripts:update()
self.computers:update()
UI.Page.draw(self)
end
if not fs.exists(SCRIPTS_PATH) then
fs.makeDir(SCRIPTS_PATH)
end
if not fs.exists(GROUPS_PATH) then
fs.makeDir(GROUPS_PATH)
end
Event.addHandler('network_attach', function()
if mainPage.enabled then
mainPage:draw()
end
end)
Event.addHandler('network_detach', function()
if mainPage.enabled then
mainPage:draw()
end
end)
function statusUpdate()
while true do
if mainPage.enabled then
local selected = mainPage.computers:getSelected()
if selected then
local computer = _G.network[selected.id]
mainPage.statusBar.values = { computer }
mainPage.statusBar:draw()
mainPage:sync()
end
end
os.sleep(1)
end
end
UI:setPage(mainPage)
Event.pullEvents(statusUpdate)
UI.term:reset()

186
sys/apps/System.lua Normal file
View File

@@ -0,0 +1,186 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
local Config = require('config')
multishell.setTitle(multishell.getCurrent(), 'System')
UI:configure('System', ...)
local env = {
path = shell.path(),
aliases = shell.aliases(),
lua_path = LUA_PATH,
}
Config.load('multishell', env)
UI.TextEntry.defaults.backgroundFocusColor = colors.black
local systemPage = UI.Page {
backgroundColor = colors.blue,
tabs = UI.Tabs {
pathTab = UI.Window {
tabTitle = 'Path',
entry = UI.TextEntry {
x = 2, y = 2, rex = -2,
limit = 256,
value = shell.path(),
shadowText = 'enter system path',
accelerators = {
enter = 'update_path',
},
},
grid = UI.Grid {
y = 4,
values = paths,
disableHeader = true,
columns = { { key = 'value' } },
autospace = true,
},
},
aliasTab = UI.Window {
tabTitle = 'Aliases',
alias = UI.TextEntry {
x = 2, y = 2, rex = -2,
limit = 32,
shadowText = 'Alias',
},
path = UI.TextEntry {
y = 3, x = 2, rex = -2,
limit = 256,
shadowText = 'Program path',
accelerators = {
enter = 'new_alias',
},
},
grid = UI.Grid {
y = 5,
values = aliases,
autospace = true,
sortColumn = 'alias',
columns = {
{ heading = 'Alias', key = 'alias' },
{ heading = 'Program', key = 'path' },
},
accelerators = {
delete = 'delete_alias',
},
},
},
infoTab = UI.Window {
tabTitle = 'Info',
labelText = UI.Text {
x = 3, y = 2,
value = 'Label'
},
label = UI.TextEntry {
x = 9, y = 2, rex = -4,
limit = 32,
value = os.getComputerLabel(),
backgroundFocusColor = colors.black,
accelerators = {
enter = 'update_label',
},
},
grid = UI.ScrollingGrid {
y = 4,
values = {
{ name = 'CC version', value = os.getVersion() },
{ name = 'Lua version', value = _VERSION },
{ name = 'MC version', value = _MC_VERSION or 'unknown' },
{ name = 'Disk free', value = Util.toBytes(fs.getFreeSpace('/')) },
{ name = 'Computer ID', value = tostring(os.getComputerID()) },
{ name = 'Day', value = tostring(os.day()) },
},
selectable = false,
backgroundColor = colors.blue,
columns = {
{ key = 'name', width = 12 },
{ key = 'value', width = UI.term.width - 15 },
},
},
},
},
notification = UI.Notification(),
accelerators = {
q = 'quit',
},
}
function systemPage.tabs.pathTab.grid:draw()
self.values = { }
for _,v in ipairs(Util.split(env.path, '(.-):')) do
table.insert(self.values, { value = v })
end
self:update()
UI.Grid.draw(self)
end
function systemPage.tabs.pathTab:eventHandler(event)
if event.type == 'update_path' then
env.path = self.entry.value
self.grid:setIndex(self.grid:getIndex())
self.grid:draw()
Config.update('multishell', env)
systemPage.notification:success('reboot to take effect')
return true
end
end
function systemPage.tabs.aliasTab.grid:draw()
self.values = { }
local aliases = { }
for k,v in pairs(env.aliases) do
table.insert(self.values, { alias = k, path = v })
end
self:update()
UI.Grid.draw(self)
end
function systemPage.tabs.aliasTab:eventHandler(event)
if event.type == 'delete_alias' then
env.aliases[self.grid:getSelected().alias] = nil
self.grid:setIndex(self.grid:getIndex())
self.grid:draw()
Config.update('multishell', env)
systemPage.notification:success('reboot to take effect')
return true
elseif event.type == 'new_alias' then
env.aliases[self.alias.value] = self.path.value
self.alias:reset()
self.path:reset()
self:draw()
self:setFocus(self.alias)
Config.update('multishell', env)
systemPage.notification:success('reboot to take effect')
return true
end
end
function systemPage.tabs.infoTab:eventHandler(event)
if event.type == 'update_label' then
os.setComputerLabel(self.label.value)
systemPage.notification:success('Label updated')
return true
end
end
function systemPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'tab_activate' then
event.activated:focusFirst()
else
return UI.Page.eventHandler(self, event)
end
return true
end
UI:setPage(systemPage)
Event.pullEvents()
UI.term:reset()

73
sys/apps/Tabs.lua Normal file
View File

@@ -0,0 +1,73 @@
require = requireInjector(getfenv(1))
local UI = require('ui')
local Event = require('event')
multishell.setTitle(multishell.getCurrent(), 'Tabs')
UI:configure('Tabs', ...)
local page = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Activate', event = 'activate' },
{ text = 'Terminate', event = 'terminate' },
},
},
grid = UI.ScrollingGrid {
y = 2,
columns = {
{ heading = 'ID', key = 'tabId' },
{ heading = 'Title', key = 'title' },
{ heading = 'Status', key = 'status' },
{ heading = 'Time', key = 'timestamp' },
},
values = multishell.getTabs(),
sortColumn = 'title',
autospace = true,
},
accelerators = {
q = 'quit',
space = 'activate',
t = 'terminate',
},
}
function page:eventHandler(event)
local t = self.grid:getSelected()
if t then
if event.type == 'activate' or event.type == 'grid_select' then
multishell.setFocus(t.tabId)
elseif event.type == 'terminate' then
multishell.terminate(t.tabId)
end
end
if event.type == 'quit' then
Event.exitPullEvents()
end
UI.Page.eventHandler(self, event)
end
function page.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
local elapsed = os.clock()-row.timestamp
if elapsed < 60 then
row.timestamp = string.format("%ds", math.floor(elapsed))
else
row.timestamp = string.format("%fm", math.floor(elapsed/6)/10)
end
if row.isDead then
row.status = 'error'
else
row.status = coroutine.status(row.co)
end
return row
end
Event.addTimer(1, true, function()
page.grid:update()
page.grid:draw()
page:sync()
end)
UI:setPage(page)
Event.pullEvents()
UI.term:reset()

328
sys/apps/Turtles.lua Normal file
View File

@@ -0,0 +1,328 @@
require = requireInjector(getfenv(1))
local UI = require('ui')
local Socket = require('socket')
local Terminal = require('terminal')
local TableDB = require('tableDB')
multishell.setTitle(multishell.getCurrent(), 'Turtles')
UI.Button.defaults.focusIndicator = ' '
UI:configure('Turtles', ...)
local options = {
turtle = { arg = 'i', type = 'number', value = -1,
desc = 'Turtle ID' },
tab = { arg = 's', type = 'string', value = 'inventory',
desc = 'Selected tab to display' },
help = { arg = 'h', type = 'flag', value = false,
desc = 'Displays the options' },
}
local SCRIPTS_PATH = '/apps/scripts'
local nullTerm = Terminal.getNullTerm(term.current())
local turtles = { }
local policies = {
{ label = 'none' },
{ label = 'digOnly' },
{ label = 'attackOnly' },
{ label = 'digAttack' },
{ label = 'turtleSafe' },
}
local itemInfoDB = TableDB({
fileName = 'items.db'
})
itemInfoDB:load()
local page = UI.Page {
moveUp = UI.Button {
x = 5, y = 2,
text = '/\\',
fn = 'turtle.up',
},
moveDown = UI.Button {
x = 5, y = 4,
text = '\\/',
fn = 'turtle.down',
},
moveForward = UI.Button {
x = 9, y = 3,
text = '>',
fn = 'turtle.forward',
},
moveBack = UI.Button {
x = 2, y = 3,
text = '<',
fn = 'turtle.back',
},
turnLeft = UI.Button {
x = 2, y = 6,
text = '<-',
fn = 'turtle.turnLeft',
},
turnRight = UI.Button {
x = 8, y = 6,
text = '->',
fn = 'turtle.turnRight',
},
--[[
policy = UI.Chooser {
x = 2, y = 8,
choices = {
{ name = ' None ', value = 'none' },
{ name = ' Safe ', value = 'turtleSafe' },
},
},
]]
coords = UI.Window {
x = 14, y = 2, height = 5, rex = -2,
},
tabs = UI.Tabs {
x = 1, y = 8, rey = -2,
scripts = UI.Grid {
tabTitle = 'Run',
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
columns = {
{ heading = '', key = 'label' },
},
disableHeader = true,
sortColumn = 'label',
autospace = true,
},
turtles = UI.Grid {
tabTitle = 'Sel',
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
columns = {
{ heading = 'label', key = 'label' },
{ heading = 'Dist', key = 'distance' },
{ heading = 'Status', key = 'status' },
{ heading = 'Fuel', key = 'fuel' },
},
disableHeader = true,
sortColumn = 'label',
autospace = true,
},
inventory = UI.Grid {
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
tabTitle = 'Inv',
columns = {
{ heading = '', key = 'qty', width = 2 },
{ heading = 'Inventory', key = 'id', width = UI.term.width - 5 },
},
disableHeader = true,
sortColumn = 'index',
},
policy = UI.Grid {
tabTitle = 'Mod',
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
columns = {
{ heading = 'label', key = 'label' },
},
values = policies,
disableHeader = true,
sortColumn = 'label',
autospace = true,
},
},
statusBar = UI.StatusBar(),
notification = UI.Notification(),
accelerators = {
q = 'quit',
},
}
function page:enable(turtle)
self.turtle = turtle
UI.Page.enable(self)
end
function page:runFunction(script, nowrap)
local socket = Socket.connect(self.turtle.id, 161)
if not socket then
self.notification:error('Unable to connect')
return
end
if not nowrap then
script = 'turtle.run(' .. script .. ')'
end
socket:write({ type = 'script', args = script })
socket:close()
end
function page:runScript(scriptName)
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)
end
function page.coords:draw()
local t = self.parent.turtle
if t then
self:clear()
self:setCursorPos(1, 1)
self:print(string.format('%s\nx: %d\ny: %d\nz: %d\nFuel: %s\n',
t.coordSystem, t.point.x, t.point.y, t.point.z, Util.toBytes(t.fuel)))
end
end
--[[ Inventory Tab ]]--
function page.tabs.inventory:getRowTextColor(row, selected)
if page.turtle and row.selected then
return colors.yellow
end
return UI.Grid.getRowTextColor(self, row, selected)
end
function page.tabs.inventory:draw()
local t = page.turtle
Util.clear(self.values)
if t then
for _,v in ipairs(t.inventory) do
if v.qty > 0 then
table.insert(self.values, v)
if v.index == t.slotIndex then
v.selected = true
end
if v.id then
local item = itemInfoDB:get({ v.id, v.dmg })
if item then
v.id = item.displayName
else
v.id = v.id:gsub('.*:(.*)', '%1')
end
end
end
end
end
self:adjustWidth()
self:update()
UI.Grid.draw(self)
end
function page.tabs.inventory:eventHandler(event)
if event.type == 'grid_select' then
local fn = string.format('turtle.select(%d)', event.selected.index)
page:runFunction(fn)
else
return UI.Grid.eventHandler(self, event)
end
return true
end
function page.tabs.scripts:draw()
Util.clear(self.values)
local files = fs.list(SCRIPTS_PATH)
for _,f in pairs(files) do
table.insert(self.values, { label = f, path = fs.combine(SCRIPTS_PATH, f) })
end
self:update()
UI.Grid.draw(self)
end
function page.tabs.scripts:eventHandler(event)
if event.type == 'grid_select' then
page:runScript(event.selected.label)
else
return UI.Grid.eventHandler(self, event)
end
return true
end
function page.tabs.turtles:getDisplayValues(row)
row = Util.shallowCopy(row)
if row.fuel then
row.fuel = Util.toBytes(row.fuel)
end
if row.distance then
row.distance = Util.round(row.distance, 1)
end
return row
end
function page.tabs.turtles:draw()
Util.clear(self.values)
for _,v in pairs(network) do
if v.fuel then
table.insert(self.values, v)
end
end
self:update()
UI.Grid.draw(self)
end
function page.tabs.turtles:eventHandler(event)
if event.type == 'grid_select' then
page.turtle = event.selected
else
return UI.Grid.eventHandler(self, event)
end
return true
end
function page.statusBar:draw()
local t = self.parent.turtle
if t then
local status = string.format('%s [ %s ]', t.status, Util.round(t.distance, 2))
self:setStatus(status, true)
end
UI.StatusBar.draw(self)
end
function page:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
elseif event.type == 'button_press' then
if event.button.fn then
self:runFunction(event.button.fn, event.button.nowrap)
elseif event.button.script then
self:runScript(event.button.script)
end
else
return UI.Page.eventHandler(self, event)
end
return true
end
function page:enable()
UI.Page.enable(self)
-- self.tabs:activateTab(page.tabs.turtles)
end
local function updateThread()
while true do
if page.turtle then
local t = _G.network[page.turtle.id]
page.turtle = t
page:draw()
page:sync()
end
os.sleep(1)
end
end
if not Util.getOptions(options, { ... }, true) then
return
end
if options.turtle.value >= 0 then
for i = 1, 10 do
page.turtle = _G.network[options.turtle.value]
if page.turtle then
break
end
os.sleep(1)
end
end
UI:setPage(page)
page.tabs:activateTab(page.tabs[options.tab.value])
UI:pullEvents(updateThread)
UI.term:reset()

35
sys/apps/base64dl.lua Normal file
View File

@@ -0,0 +1,35 @@
require = requireInjector(getfenv(1))
Base64 = require('base64')
local args = { ... }
if not args[2] then
error('Syntax: base64dl <file name> <url>')
end
local c = http.get(args[2])
if not c then
error('unable to open url')
end
local data = c.readAll()
c.close()
print('size: ' .. #data)
local decoded = Base64.decode(data)
print('decoded: ' .. #decoded)
local file = io.open(shell.resolve(args[1]), "wb")
if not file then
error('Unable to open ' .. args[1], 2)
end
for k,b in ipairs(decoded) do
if (k % 1000) == 0 then
os.sleep(0)
end
file:write(b)
end
file:close()
print('done')

2155
sys/apps/builder.lua Normal file

File diff suppressed because it is too large Load Diff

24
sys/apps/cat.lua Normal file
View File

@@ -0,0 +1,24 @@
local args = { ... }
if #args < 1 then
error('cat <filename>')
end
local fileName = shell.resolve(args[1])
if not fs.exists(fileName) then
error('not a file: ' .. args[1])
end
local file = fs.open(fileName, 'r')
if not file then
error('unable to open ' .. args[1])
end
while true do
local line = file.readLine()
if not line then
break
end
print(line)
end
file.close()

1270
sys/apps/edit.lua Normal file

File diff suppressed because it is too large Load Diff

41
sys/apps/itemsDB.lua Normal file
View File

@@ -0,0 +1,41 @@
local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local RefinedProvider = require('refinedProvider')
local TableDB = require('tableDB')
local controller = RefinedProvider()
if not controller:isValid() then
error('Refined storage controller not found')
end
local itemInfoDB = TableDB({
fileName = 'items.db'
})
itemInfoDB:load()
local items = controller:listItems()
local keys = {
'fields',
'damage',
'displayName',
'maxCount',
'maxDamage',
'name',
'nbtHash',
'rawName',
}
for _, item in pairs(items) do
local t = { }
for _,key in pairs(keys) do
t[key] = item[key]
end
itemInfoDB:add({ item.name, item.damage, item.nbtHash }, t)
end
itemInfoDB:flush()

102
sys/apps/logMonitor.lua Normal file
View File

@@ -0,0 +1,102 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local Message = require('message')
local UI = require('ui')
multishell.setTitle(multishell.getCurrent(), 'Log Monitor')
if not device.wireless_modem then
error('Wireless modem is required')
end
device.wireless_modem.open(59998)
local ids = { }
local messages = { }
local terminal = UI.term
if device.openperipheral_bridge then
UI.Glasses = require('glasses')
terminal = UI.Glasses({
x = 4,
y = 175,
height = 40,
width = 64,
textScale = .5,
backgroundOpacity = .65,
})
elseif device.monitor then
terminal = UI.Device({
deviceType = 'monitor',
textScale = .5
})
end
terminal:clear()
function getClient(id)
if not ids[id] then
ids[id] = {
titleBar = UI.TitleBar({ title = 'ID: ' .. id, parent = terminal }),
scrollingText = UI.ScrollingText({ parent = terminal })
}
local clientCount = Util.size(ids)
local clientHeight = math.floor((terminal.height - clientCount) / clientCount)
terminal:clear()
local y = 1
for k,v in pairs(ids) do
v.titleBar.y = y
y = y + 1
v.scrollingText.height = clientHeight
v.scrollingText.y = y
y = y + clientHeight
v.scrollingText:clear()
v.titleBar:draw()
v.scrollingText:draw()
end
end
return ids[id]
end
local function logWriter()
while true do
os.pullEvent('logMessage')
local t = { }
while #messages > 0 do
local msg = messages[1]
table.remove(messages, 1)
local client = getClient(msg.id)
client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text))
t[msg.id] = client
end
for _,client in pairs(t) do
client.scrollingText:draw()
end
terminal:sync()
end
end
Message.addHandler('log', function(h, id, msg)
table.insert(messages, { id = id, text = msg.contents })
os.queueEvent('logMessage')
end)
Event.addHandler('monitor_touch', function()
terminal:reset()
ids = { }
end)
Event.addHandler('mouse_click', function()
terminal:reset()
ids = { }
end)
Event.addHandler('char', function()
Event.exitPullEvents()
end)
Event.pullEvents(logWriter)
terminal:reset()

24
sys/apps/mirror.lua Normal file
View File

@@ -0,0 +1,24 @@
require = requireInjector(getfenv(1))
local Terminal = require('terminal')
local args = { ... }
local mon = device[table.remove(args, 1) or 'monitor']
if not mon then
error('mirror: Invalid device')
end
mon.clear()
mon.setTextScale(.5)
mon.setCursorPos(1, 1)
local oterm = Terminal.copy(term.current())
Terminal.mirror(term.current(), mon)
term.current().getSize = mon.getSize
if #args > 0 then
shell.run(unpack(args))
Terminal.copy(oterm, term.current())
mon.setCursorBlink(false)
end

83
sys/apps/mirrorClient.lua Normal file
View File

@@ -0,0 +1,83 @@
require = requireInjector(getfenv(1))
local Socket = require('socket')
local Terminal = require('terminal')
local Logger = require('logger')
local process = require('process')
Logger.setScreenLogging()
local remoteId
local args = { ... }
if #args == 1 then
remoteId = tonumber(args[1])
else
print('Enter host ID')
remoteId = tonumber(read())
end
if not remoteId then
error('Syntax: mirrorClient <host ID>')
end
local function wrapTerm(socket)
local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',
'setTextColor', 'setTextColour', 'setBackgroundColor',
'setBackgroundColour', 'scroll', 'setCursorBlink', }
socket.term = multishell.term
socket.oldTerm = Util.shallowCopy(socket.term)
for _,k in pairs(methods) do
socket.term[k] = function(...)
if not socket.queue then
socket.queue = { }
os.queueEvent('mirror_flush')
end
table.insert(socket.queue, {
f = k,
args = { ... },
})
socket.oldTerm[k](...)
end
end
end
while true do
print('connecting...')
local socket
while true do
socket = Socket.connect(remoteId, 5901)
if socket then
break
end
os.sleep(3)
end
print('connected')
wrapTerm(socket)
os.queueEvent('term_resize')
while true do
local e = process:pullEvent('mirror_flush')
if e == 'terminate' then
break
end
if not socket.connected then
break
end
if socket.queue then
socket:write(socket.queue)
socket.queue = nil
end
end
for k,v in pairs(socket.oldTerm) do
socket.term[k] = v
end
socket:close()
end

55
sys/apps/mirrorHost.lua Normal file
View File

@@ -0,0 +1,55 @@
require = requireInjector(getfenv(1))
local Socket = require('socket')
local Logger = require('logger')
local process = require('process')
Logger.setScreenLogging()
local args = { ... }
local mon = device[args[1] or 'monitor']
if not mon then
error('Monitor not attached')
end
mon.setBackgroundColor(colors.black)
mon.clear()
while true do
local socket = Socket.server(5901)
print('mirror: connection from ' .. socket.dhost)
local updateThread = process:newThread('updateThread', function()
while true do
local data = socket:read()
if not data then
break
end
for _,v in ipairs(data) do
mon[v.f](unpack(v.args))
end
end
end)
-- ensure socket is connected
process:newThread('pinger', function()
while true do
os.sleep(3)
if not socket:ping() then
break
end
end
end)
while true do
process:pullEvent('modem_message')
if updateThread:isDead() then
break
end
end
print('connection lost')
socket:close()
end

605
sys/apps/multishell Normal file
View File

@@ -0,0 +1,605 @@
-- Default label
if not os.getComputerLabel() then
local id = os.getComputerID()
if turtle then
os.setComputerLabel('turtle_' .. id)
elseif pocket then
os.setComputerLabel('pocket_' .. id)
else
os.setComputerLabel('computer_' .. id)
end
end
multishell.term = term.current()
local defaultEnv = Util.shallowCopy(getfenv(1))
require = requireInjector(getfenv(1))
local Config = require('config')
-- Begin multishell
local parentTerm = term.current()
local w,h = parentTerm.getSize()
local tabs = {}
local currentTab
local _tabId = 0
local overviewTab
local runningTab
local tabsDirty = false
local config = {
standard = {
focusTextColor = colors.lightGray,
focusBackgroundColor = colors.gray,
textColor = colors.black,
backgroundColor = colors.lightGray,
tabBarTextColor = colors.black,
tabBarBackgroundColor = colors.lightGray,
},
color = {
focusTextColor = colors.white,
focusBackgroundColor = colors.brown,
textColor = colors.gray,
backgroundColor = colors.brown,
tabBarTextColor = colors.lightGray,
tabBarBackgroundColor = colors.brown,
},
-- path = '.:/apps:' .. shell.path():sub(3),
path = 'usr/apps:sys/apps:' .. shell.path(),
}
Config.load('multishell', config)
shell.setPath(config.path)
if config.aliases then
for k in pairs(shell.aliases()) do
shell.clearAlias(k)
end
for k,v in pairs(config.aliases) do
shell.setAlias(k, v)
end
end
local _colors = config.standard
if parentTerm.isColor() then
_colors = config.color
end
local function redrawMenu()
if not tabsDirty then
os.queueEvent('multishell', 'draw')
tabsDirty = true
end
end
-- Draw menu
local function draw()
tabsDirty = false
parentTerm.setBackgroundColor( _colors.tabBarBackgroundColor )
if currentTab and currentTab.isOverview then
parentTerm.setTextColor( _colors.focusTextColor )
else
parentTerm.setTextColor( _colors.tabBarTextColor )
end
parentTerm.setCursorPos( 1, 1 )
parentTerm.clearLine()
parentTerm.write('+')
local tabX = 2
local function compareTab(a, b)
return a.tabId < b.tabId
end
for _,tab in Util.spairs(tabs, compareTab) do
if tab.hidden and tab ~= currentTab or tab.isOverview then
tab.sx = nil
tab.ex = nil
else
tab.sx = tabX + 1
tab.ex = tabX + #tab.title
tabX = tabX + #tab.title + 1
end
end
for _,tab in Util.spairs(tabs) do
if tab.sx then
if tab == currentTab then
parentTerm.setTextColor(_colors.focusTextColor)
parentTerm.setBackgroundColor(_colors.focusBackgroundColor)
else
parentTerm.setTextColor(_colors.textColor)
parentTerm.setBackgroundColor(_colors.backgroundColor)
end
parentTerm.setCursorPos(tab.sx, 1)
parentTerm.write(tab.title)
end
end
if currentTab and not currentTab.isOverview then
parentTerm.setTextColor(_colors.textColor)
parentTerm.setBackgroundColor(_colors.backgroundColor)
parentTerm.setCursorPos( w, 1 )
parentTerm.write('*')
end
if currentTab then
currentTab.window.restoreCursor()
end
end
local function selectTab( tab )
if not tab then
for _,ftab in pairs(tabs) do
if not ftab.hidden then
tab = ftab
break
end
end
end
if not tab then
tab = overviewTab
end
if currentTab and currentTab ~= tab then
currentTab.window.setVisible(false)
if tab and not currentTab.hidden then
tab.previousTabId = currentTab.tabId
end
end
if tab then
currentTab = tab
tab.window.setVisible(true)
end
end
local function resumeTab(tab, event, eventData)
if not tab or coroutine.status(tab.co) == 'dead' then
return
end
if not tab.filter or tab.filter == event or event == "terminate" then
eventData = eventData or { }
term.redirect(tab.terminal)
local previousTab = runningTab
runningTab = tab
local ok, result = coroutine.resume(tab.co, event, unpack(eventData))
tab.terminal = term.current()
if ok then
tab.filter = result
else
printError(result)
end
runningTab = previousTab
return ok, result
end
end
local function nextTabId()
_tabId = _tabId + 1
return _tabId
end
local function launchProcess(tab)
tab.tabId = nextTabId()
tab.timestamp = os.clock()
tab.window = window.create(parentTerm, 1, 2, w, h - 1, false)
tab.terminal = tab.window
tab.env = Util.shallowCopy(tab.env or defaultEnv)
tab.co = coroutine.create(function()
local result, err
if tab.fn then
result, err = Util.runFunction(tab.env, tab.fn, table.unpack(tab.args or { } ))
elseif tab.path then
result, err = os.run(tab.env, tab.path, table.unpack(tab.args or { } ))
else
err = 'multishell: invalid tab'
end
if not result and err ~= 'Terminated' then
if err then
printError(tostring(err))
end
printError('Press enter to exit')
tab.isDead = true
while true do
local e, code = os.pullEventRaw('key')
if e == 'terminate' or e == 'key' and code == keys.enter then
if tab.isOverview then
os.queueEvent('multishell', 'terminate')
end
break
end
end
end
tabs[tab.tabId] = nil
if tab == currentTab then
local previousTab
if tab.previousTabId then
previousTab = tabs[tab.previousTabId]
end
selectTab(previousTab)
end
redrawMenu()
end)
tabs[tab.tabId] = tab
resumeTab(tab)
return tab
end
local function resizeWindows()
local windowY = 2
local windowHeight = h-1
local keys = Util.keys(tabs)
for _,key in pairs(keys) do
local tab = tabs[key]
local x,y = tab.window.getCursorPos()
if y > windowHeight then
tab.window.scroll( y - windowHeight )
tab.window.setCursorPos( x, windowHeight )
end
tab.window.reposition( 1, windowY, w, windowHeight )
end
-- Pass term_resize to all processes
local keys = Util.keys(tabs)
for _,key in pairs(keys) do
resumeTab(tabs[key], "term_resize")
end
end
local control
local hotkeys = { }
local function processKeyEvent(event, code)
if event == 'key_up' then
if code == keys.leftCtrl or code == keys.rightCtrl then
control = false
end
elseif event == 'char' then
control = false
elseif event == 'key' then
if code == keys.leftCtrl or code == keys.rightCtrl then
control = true
elseif control then
local hotkey = hotkeys[code]
control = false
if hotkey then
hotkey()
end
end
end
end
function multishell.addHotkey(code, fn)
hotkeys[code] = fn
end
function multishell.removeHotkey(code)
hotkeys[code] = nil
end
function multishell.getFocus()
return currentTab.tabId
end
function multishell.setFocus(tabId)
local tab = tabs[tabId]
if tab then
selectTab(tab)
redrawMenu()
return true
end
return false
end
function multishell.getTitle(tabId)
local tab = tabs[tabId]
if tab then
return tab.title
end
end
function multishell.setTitle(tabId, sTitle)
local tab = tabs[tabId]
if tab then
tab.title = sTitle or ''
redrawMenu()
end
end
function multishell.getCurrent()
if runningTab then
return runningTab.tabId
end
end
function multishell.getTab(tabId)
return tabs[tabId]
end
function multishell.terminate(tabId)
local tab = tabs[tabId]
if tab and not tab.isOverview then
if coroutine.status(tab.co) ~= 'dead' then
--os.queueEvent('multishell', 'terminate', tab)
resumeTab(tab, "terminate")
else
tabs[tabId] = nil
if tab == currentTab then
local previousTab
if tab.previousTabId then
previousTab = tabs[tab.previousTabId]
end
selectTab(previousTab)
end
redrawMenu()
end
end
end
function multishell.getTabs()
return tabs
end
function multishell.launch( tProgramEnv, sProgramPath, ... )
-- backwards compatibility
return multishell.openTab({
env = tProgramEnv,
path = sProgramPath,
args = { ... },
})
end
function multishell.openTab(tab)
if not tab.title and tab.path then
tab.title = fs.getName(tab.path)
end
tab.title = tab.title or 'untitled'
local previousTerm = term.current()
launchProcess(tab)
term.redirect(previousTerm)
if tab.hidden then
if coroutine.status(tab.co) == 'dead' or tab.isDead then
tab.hidden = false
end
elseif tab.focused then
multishell.setFocus(tab.tabId)
else
redrawMenu()
end
return tab.tabId
end
function multishell.hideTab(tabId)
local tab = tabs[tabId]
if tab then
tab.hidden = true
redrawMenu()
end
end
function multishell.unhideTab(tabId)
local tab = tabs[tabId]
if tab then
tab.hidden = false
redrawMenu()
end
end
function multishell.getCount()
local count
for _,tab in pairs(tabs) do
count = count + 1
end
return count
end
-- control-o - overview
multishell.addHotkey(24, function()
multishell.setFocus(overviewTab.tabId)
end)
-- control-backspace
multishell.addHotkey(14, function()
local tabId = multishell.getFocus()
local tab = tabs[tabId]
if not tab.isOverview then
os.queueEvent('multishell', 'terminateTab', tabId)
tab = Util.shallowCopy(tab)
tab.isDead = false
tab.focused = true
multishell.openTab(tab)
end
end)
-- control-tab - next tab
multishell.addHotkey(15, function()
local function compareTab(a, b)
return a.tabId < b.tabId
end
local visibleTabs = { }
for _,tab in Util.spairs(tabs, compareTab) do
if not tab.hidden then
table.insert(visibleTabs, tab)
end
end
for k,tab in ipairs(visibleTabs) do
if tab.tabId == currentTab.tabId then
if k < #visibleTabs then
multishell.setFocus(visibleTabs[k + 1].tabId)
return
end
end
end
if #visibleTabs > 0 then
multishell.setFocus(visibleTabs[1].tabId)
end
end)
local function startup()
local hasError
local function runDir(directory, desc, open)
if not fs.exists(directory) then
return
end
local files = fs.list(directory)
table.sort(files)
for _,file in ipairs(files) do
print(desc .. file)
os.sleep(0)
local result, err = open(directory .. '/' .. file)
if not result then
printError(err)
hasError = true
end
end
end
runDir('sys/extensions', '[ ext ] ', shell.run)
local overviewId = multishell.openTab({
path = 'sys/apps/Overview.lua',
focused = true,
hidden = true,
isOverview = true,
})
overviewTab = tabs[overviewId]
runDir('sys/services', '[ svc ] ', shell.openHiddenTab)
runDir('sys/autorun', '[ aut ] ', shell.run)
runDir('usr/autorun', '[ aut ] ', shell.run)
if hasError then
error('An autorun program has errored')
end
end
-- Begin
parentTerm.clear()
multishell.openTab({
focused = true,
fn = startup,
env = defaultEnv,
title = 'Autorun',
})
if not overviewTab or coroutine.status(overviewTab.co) == 'dead' then
--error('Overview aborted')
end
if not currentTab then
multishell.setFocus(overviewTab.tabId)
end
draw()
while true do
-- Get the event
local tEventData = { os.pullEventRaw() }
local sEvent = table.remove(tEventData, 1)
if sEvent == 'key_up' then
processKeyEvent(sEvent, tEventData[1])
end
if sEvent == "term_resize" then
-- Resize event
w,h = parentTerm.getSize()
resizeWindows()
redrawMenu()
elseif sEvent == 'multishell' then
local action = tEventData[1]
if action == 'terminate' then
break
elseif action == 'terminateTab' then
multishell.terminate(tEventData[2])
elseif action == 'draw' then
draw()
end
elseif sEvent == "char" or
sEvent == "key" or
sEvent == "paste" or
sEvent == "terminate" then
processKeyEvent(sEvent, tEventData[1])
-- Keyboard event - Passthrough to current process
resumeTab(currentTab, sEvent, tEventData)
elseif sEvent == "mouse_click" then
local button, x, y = tEventData[1], tEventData[2], tEventData[3]
if y == 1 and os.locked then
-- ignore
elseif y == 1 then
-- Switch process
local w, h = parentTerm.getSize()
if x == 1 then
multishell.setFocus(overviewTab.tabId)
elseif x == w then
if currentTab then
multishell.terminate(currentTab.tabId)
end
else
for _,tab in pairs(tabs) do
if not tab.hidden and tab.sx then
if x >= tab.sx and x <= tab.ex then
multishell.setFocus(tab.tabId)
break
end
end
end
end
elseif currentTab then
-- Passthrough to current process
resumeTab(currentTab, sEvent, { button, x, y-1 })
end
elseif sEvent == "mouse_drag" or sEvent == "mouse_scroll" then
-- Other mouse event
local p1, x, y = tEventData[1], tEventData[2], tEventData[3]
if currentTab and (y ~= 1) then
if currentTab.terminal.scrollUp then
if p1 == -1 then
currentTab.terminal.scrollUp()
else
currentTab.terminal.scrollDown()
end
else
-- Passthrough to current process
resumeTab(currentTab, sEvent, { p1, x, y-1 })
end
end
else
-- Other event
-- Passthrough to all processes
local keys = Util.keys(tabs)
for _,key in pairs(keys) do
resumeTab(tabs[key], sEvent, tEventData)
end
end
end

10
sys/apps/password.lua Normal file
View File

@@ -0,0 +1,10 @@
require = requireInjector(getfenv(1))
local SHA1 = require('sha1')
local Terminal = require('terminal')
local password = Terminal.readPassword('Enter new password: ')
if password then
os.updatePassword(SHA1.sha1(password))
print('Password updated')
end

343
sys/apps/pickup.lua Normal file
View File

@@ -0,0 +1,343 @@
require = requireInjector(getfenv(1))
local GPS = require('gps')
local Socket = require('socket')
local MEProvider = require('meProvider')
local Logger = require('logger')
local Point = require('point')
local process = require('process')
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
turtle.clearMoveCallback()
local gps = GPS.getPointAndHeading()
if not gps then
error('could not get gps location')
end
turtle.setPoint(gps)
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 fuel = {
item = {
id = 'minecraft:coal',
dmg = 0,
},
qty = 64
}
local slots
turtle.setMoveCallback(function(action, pt)
if slots then
for _,slot in pairs(slots) do
if turtle.getItemCount(slot.index) ~= slot.qty then
printError('Slots changed')
process:terminate()
end
end
end
end)
function refuel()
if turtle.getFuelLevel() < 5000 then
print('refueling')
turtle.status = 'refueling'
gotoPoint(chestPt, true)
dropOff(chestPt)
while turtle.getFuelLevel() < 5000 do
turtle.select(1)
meProvider:provide(fuel.item, fuel.qty, 1)
turtle.refuel(64)
print(turtle.getFuelLevel())
os.sleep(1)
end
end
end
function pickUp(pt)
turtle.status = 'picking up'
gotoPoint(pt, true)
while true do
if not turtle.selectOpenSlot() then
dropOff(chestPt)
gotoPoint(pt, true)
end
turtle.select(1)
if not turtle.suckDown(64) then
return
end
end
end
function dropOff(pt)
if turtle.selectSlotWithItems() then
gotoPoint(pt, true)
turtle.emptyInventory(turtle.dropDown)
if pt == chestPt then
print('refreshing items')
items = meProvider:refresh()
end
end
end
function gotoPoint(pt, doDetect)
slots = turtle.getInventory()
while not turtle.pathfind(pt, blocks) do
if turtle.abort then
error('aborted')
end
turtle.status = 'blocked'
os.sleep(5)
end
if doDetect and not turtle.detectDown() then
error('Missing target')
end
end
function checkCell(pt)
if not turtle.selectOpenSlot() then
dropOff(chestPt)
end
print('checking cell')
turtle.status = 'recharging'
gotoPoint(pt, true)
local c = peripheral.wrap('bottom')
local energy = c.getMaxEnergyStored() -
c.getEnergyStored()
if energy > 20000 then
print('charging cell')
turtle.selectOpenSlot()
turtle.digDown()
gotoPoint(chargePt, true)
turtle.dropDown()
os.sleep(energy / 20000)
turtle.suckDown()
print('replacing cell')
gotoPoint(pt)
if not turtle.placeDown() then
error('could not place down cell')
end
end
end
function fluid(points)
print('checking fluid')
turtle.status = 'fluiding'
gotoPoint(points.source, true)
turtle.select(1)
turtle.digDown()
gotoPoint(points.target)
if not turtle.placeDown() then
error('could not place fluid container')
end
os.sleep(5)
turtle.digDown()
gotoPoint(points.source)
turtle.placeDown()
end
function refill(entry)
dropOff(chestPt)
turtle.status = 'refilling'
gotoPoint(chestPt)
for _,item in pairs(entry.items) do
meProvider:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
end
if turtle.selectSlotWithItems() then
if entry.point then
dropOff(entry.point)
end
end
end
function oldRefill(points)
gotoPoint(points.source)
repeat until not turtle.suckDown(64)
if points.target then
dropOff(points.target)
end
if points.targets then
for k,target in pairs(points.targets) do
dropOff(target)
end
end
dropOff(points.source)
dropOff(chestPt)
end
local function makeKey(pt)
return string.format('%d:%d:%d', pt.x, pt.y, pt.z)
end
local function pickupHost(socket)
while true do
local data = socket:read()
if not data then
print('pickup: closing connection to ' .. socket.dhost)
return
end
print('command: ' .. data.type)
if data.type == 'pickup' then
local key = makeKey(data.point)
pickups[key] = data.point
Util.writeTable('pickup.tbl', pickups)
socket:write( { type = "response", response = 'added' })
elseif data.type == 'items' then
socket:write( { type = "response", response = items })
elseif data.type == 'refill' then
local key = makeKey(data.entry.point)
refills[key] = data.entry
Util.writeTable('refills.tbl', refills)
socket:write( { type = "response", response = 'added' })
elseif data.type == 'setPickup' then
chestPt = data.point
turtle.storeLocation('chest', chestPt)
socket:write( { type = "response", response = 'Location set' })
elseif data.type == 'setRecharge' then
chargePt = data.point
turtle.storeLocation('charge', chargePt)
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)
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
Util.writeTable('refills.tbl', refills)
Util.writeTable('cells.tbl', cells)
Util.writeTable('fluids.tbl', fluids)
Util.writeTable('pickup.tbl', pickups)
socket:write( { type = "response", response = 'cleared' })
else
print('unknown command')
end
end
end
process:newThread('pickup', function()
while true do
print('waiting for connection on port 5222')
local socket = Socket.server(5222)
print('pickup: connection from ' .. socket.dhost)
process:newThread('pickup_connection', function() pickupHost(socket) end)
end
end)
local function eachEntry(t, fn)
local keys = Util.keys(t)
for _,key in pairs(keys) do
if t[key] then
if turtle.abort then
return
end
fn(t[key])
end
end
end
local function eachClosestEntry(t, fn)
local points = { }
for k,v in pairs(t) do
v = Util.shallowCopy(v)
v.key = k
table.insert(points, v)
end
while not Util.empty(points) do
local closest = Point.closest(turtle.point, points)
if turtle.abort then
return
end
if t[closest.key] then
fn(closest)
end
for k,v in pairs(points) do
if v.key == closest.key then
table.remove(points, k)
break
end
end
end
end
refuel()
turtle.abort = false
local deliveryThread = process:newThread('deliveries', function()
while true do
if chestPt then
eachClosestEntry(pickups, pickUp)
eachEntry(refills, refill)
refuel()
end
eachEntry(fluids, fluid)
if chargePt then
eachEntry(cells, checkCell)
end
print('sleeping')
turtle.status = 'sleeping'
if turtle.abort then
printError('aborted')
break
end
os.sleep(60)
end
end)
turtle.run(function()
while true do
local e = process:pullEvent()
if e == 'terminate' or deliveryThread:isDead() then
break
end
end
end)
process:threadEvent('terminate')

229
sys/apps/pickupRemote.lua Normal file
View File

@@ -0,0 +1,229 @@
if not device.wireless_modem then
error('Wireless modem is required')
end
require = requireInjector(getfenv(1))
local GPS = require('gps')
local Event = require('event')
local UI = require('ui')
local Socket = require('socket')
multishell.setTitle(multishell.getCurrent(), 'Pickup Remote')
local id
local mainPage = UI.Page({
menu = UI.Menu({
centered = true,
y = 2,
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 recharge location', event = 'setRecharge', help = 'Recharge this cell' },
{ prompt = 'Clear', event = 'clear', help = 'Remove this location' },
},
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'quit',
},
})
local refillPage = UI.Page({
menuBar = UI.MenuBar({
y = 1,
buttons = {
{ text = 'Done', event = 'done', help = 'Pickup items from this location' },
{ text = 'Back', event = 'back', help = 'Recharge this cell' },
},
}),
grid1 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
{ heading = 'Qty', key = 'fQty', width = 5 },
},
sortColumn = 'name',
height = 8,
y = 3,
}),
grid2 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
{ heading = 'Qty', key = 'qty', width = 5 },
},
sortColumn = 'name',
height = 4,
y = 12,
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'quit',
},
})
refillPage.menuBar:add({
filter = UI.TextEntry({
x = UI.term.width-10,
width = 10,
})
})
local function sendCommand(cmd)
local socket = Socket.connect(id, 5222)
if not socket then
mainPage.statusBar:timedStatus('Unable to connect', 3)
return
end
socket:write(cmd)
local m = socket:read(3)
socket:close()
if m then
return m.response
end
mainPage.statusBar:timedStatus('No response', 3)
end
local function getPoint()
local gpt = GPS.getPoint()
if not gpt then
mainPage.statusBar:timedStatus('Unable to get location', 3)
end
return gpt
end
function refillPage:eventHandler(event)
if event.type == 'grid_select' then
local item = {
name = event.selected.name,
id = event.selected.id,
dmg = event.selected.dmg,
qty = 0,
}
local dialog = UI.Dialog({
x = 1,
width = UI.term.width,
text = UI.Text({ x = 3, y = 3, value = 'Quantity' }),
textEntry = UI.TextEntry({ x = 14, y = 3 })
})
dialog.eventHandler = function(self, event)
if event.type == 'accept' then
local l = tonumber(self.textEntry.value)
if l and l <= 1024 and l > 0 then
item.qty = self.textEntry.value
table.insert(refillPage.grid2.values, item)
refillPage.grid2:update()
UI:setPreviousPage()
else
self.statusBar:timedStatus('Invalid Quantity', 3)
end
return true
end
return UI.Dialog.eventHandler(self, event)
end
dialog.titleBar.title = item.name
dialog:setFocus(dialog.textEntry)
UI:setPage(dialog)
elseif event.type == 'text_change' then
local text = event.text
if #text == 0 then
self.grid1.values = self.allItems
else
self.grid1.values = { }
for _,item in pairs(self.allItems) do
if string.find(item.lname, text) then
table.insert(self.grid1.values, item)
end
end
end
--self.grid:adjustWidth()
self.grid1:update()
self.grid1:setIndex(1)
self.grid1:draw()
elseif event.type == 'back' then
UI:setPreviousPage()
elseif event.type == 'done' then
UI:setPage(mainPage)
local pt = getPoint()
if pt then
local response = sendCommand({ type = 'refill', entry = { point = pt, items = self.grid2.values } })
if response then
mainPage.statusBar:timedStatus(response, 3)
end
end
elseif event.type == 'grid_focus_row' then
self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg)
self.statusBar:draw()
end
return UI.Page.eventHandler(self, event)
end
function refillPage:enable()
for _,item in pairs(self.allItems) do
item.lname = string.lower(item.name)
item.fQty = Util.toBytes(item.qty)
end
self.grid1:setValues(self.allItems)
self.menuBar.filter.value = ''
self.menuBar.filter.pos = 1
self:setFocus(self.menuBar.filter)
UI.Page.enable(self)
end
function mainPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'refill' then
local response = sendCommand({ type = 'items' })
if response then
refillPage.allItems = response
refillPage.grid2:setValues({ })
UI:setPage(refillPage)
end
elseif event.type == 'pickup' or event.type == 'setPickup' or
event.type == 'setRecharge' or event.type == 'charge' or
event.type == 'clear' then
local pt = getPoint()
if pt then
local response = sendCommand({ type = event.type, point = pt })
if response then
self.statusBar:timedStatus(response, 3)
end
end
end
return UI.Page.eventHandler(self, event)
end
local args = { ... }
if #args == 1 then
id = tonumber(args[1])
end
if not id then
error('Syntax: pickupRemote <turtle ID>')
end
UI:setPage(mainPage)
Event.pullEvents()
UI.term:reset()

539
sys/apps/recorder.lua Normal file
View File

@@ -0,0 +1,539 @@
-- +---------------------+------------+---------------------+
-- | | | |
-- | | RecGif | |
-- | | | |
-- +---------------------+------------+---------------------+
local version = "Version 1.1.6"
-- Records your terminal and saves the result as an animating GIF.
-- http://www.computercraft.info/forums2/index.php?/topic/24840-recgif/
-- ----------------------------------------------------------
-- Original code by Bomb Bloke
-- Modified to integrate with opus os
local recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput = {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, ""
local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize()
local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"}
local charW, charH, chars, resp
local filename
local calls = { }
local curCalls = { delay = 0 }
local callListCount = 0
local callCount = 0
local function showSyntax()
print('Gif Recorder by Bomb Bloke\n')
print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename')
print(' -i : show input')
print(' -s : skip last')
print(' -ld : last delay')
end
for i = #arg, 1, -1 do
local curArg = arg[i]:lower()
if curArg == "-i" then
showInput, ySize = true, ySize + 1
table.remove(arg, i)
elseif curArg == "-s" then
skipLast = true
table.remove(arg, i)
elseif curArg:sub(1, 4) == "-ld:" then
curArg = tonumber(curArg:sub(5))
if curArg then lastDelay = curArg end
table.remove(arg, i)
elseif curArg == '-?' then
showSyntax()
return
elseif i ~= #arg then
showSyntax()
printError('\nInvalid argument')
return
end
end
print('Press control-p to stop recording')
local filename = arg[#arg]
if not filename then
print('Enter file name:')
filename = read()
end
if #filename == 0 then
showSyntax()
print()
error('Invalid file name')
end
print('Initializing...')
fs.mount('.recGif', 'ramfs', 'directory')
fs.mount('.recGif/GIF', 'urlfs', 'http://pastebin.com/raw/5uk9uRjC')
fs.mount('.recGif/package', 'urlfs', 'http://pastebin.com/raw/cUYTGbpb')
-- don't pollute global env
local function loadAPI(filename, env)
local apiEnv = Util.shallowCopy(env)
apiEnv.shell = nil
apiEnv.multishell = nil
setmetatable(apiEnv, { __index = _G })
local fn = loadfile(filename, apiEnv)
fn()
return apiEnv
end
package = loadAPI('.recGif/package', getfenv(1))
GIF = loadAPI('.recGif/GIF', getfenv(1))
local oldDir = shell.dir()
shell.setDir('.recGif')
shell.run("package get Y0eLUPtr")
shell.setDir(oldDir)
local function snooze()
local myEvent = tostring({})
os.queueEvent(myEvent)
os.pullEvent(myEvent)
end
local function safeString(text)
local newText = {}
for i = 1, #text do
local val = text:byte(i)
newText[i] = (val > 31 and val < 127) and val or 63
end
return string.char(unpack(newText))
end
local function safeCol(text, subst)
local newText = {}
for i = 1, #text do
local val = text:sub(i, i)
newText[i] = greys[val] and val or subst
end
return table.concat(newText)
end
-- Build a terminal that records stuff:
recTerm = multishell.term
for key, func in pairs(oldTerm) do
recTerm[key] = function(...)
local result = { func(...) }
if callCount == 0 then
os.queueEvent('capture_frame')
end
callCount = callCount + 1
curCalls[callCount] = { key, ... }
return unpack(result)
end
end
local tabId = multishell.getCurrent()
multishell.addHotkey(25, function()
os.queueEvent('recorder_stop')
end)
local tabs = multishell.getTabs()
for _,tab in pairs(tabs) do
if tab.isOverview then
multishell.hideTab(tabId)
multishell.setFocus(tab.tabId)
os.queueEvent('term_resize')
break
end
end
local curTime = os.clock() - 1
while true do
local event = { os.pullEventRaw() }
if event[1] == 'recorder_stop' or event[1] == 'terminate' then
break
end
if event[1] == 'capture_frame' then
local newTime = os.clock()
if callListCount > 0 then
calls[callListCount].delay = (newTime - curTime)
end
curTime = newTime
callListCount = callListCount + 1
calls[callListCount] = curCalls
curCalls, callCount = { delay = 0 }, 0
end
end
multishell.removeHotkey(25)
for k,fn in pairs(oldTerm) do
multishell.term[k] = fn
end
multishell.unhideTab(tabId)
multishell.setFocus(tabId)
if #calls[#calls] == 0 then calls[#calls] = nil end
if skipLast and #calls > 1 then calls[#calls] = nil end
calls[#calls].delay = lastDelay
print(string.format("Encoding %d frames...", #calls))
--Util.writeTable('tmp/raw.txt', calls)
-- Perform a quick re-parse of the recorded data (adding frames for when the cursor blinks):
do
local callListCount, tempCalls, blink, oldBlink, curBlink, blinkDelay = 1, {}, false, false, true, 0
for i = 1, #calls - 1 do
curCalls = calls[i]
tempCalls[callListCount] = curCalls
for j = 1, #curCalls do if curCalls[j][1] == "setCursorBlink" then blink = curCalls[j][2] end end
if blink then
if blinkDelay == 0 then
curCalls[#curCalls + 1] = {"toggleCur", curBlink}
blinkDelay, curBlink = 0.4, not curBlink
end
while tempCalls[callListCount].delay > blinkDelay do
local remainder = tempCalls[callListCount].delay - blinkDelay
tempCalls[callListCount].delay = blinkDelay
callListCount = callListCount + 1
tempCalls[callListCount] = {{"toggleCur", curBlink}, ["delay"] = remainder}
blinkDelay, curBlink = 0.4, not curBlink
end
blinkDelay = blinkDelay - tempCalls[callListCount].delay
else
if oldBlink then curCalls[#curCalls + 1] = {"toggleCur", false} end
blinkDelay = (curCalls.delay - blinkDelay) % 0.4
end
callListCount, oldBlink = callListCount + 1, blink
end
tempCalls[callListCount] = calls[#calls]
tempCalls[callListCount][#tempCalls[callListCount] + 1] = {"toggleCur", false}
calls, curCalls = tempCalls, nil
end
snooze()
-- Load font data:
do
local ascii, counter = GIF.toPaintutils(GIF.flattenGIF(GIF.loadGIF(".recGif/ascii.gif"))), 0
local newFont, ybump, xbump = #ascii ~= #ascii[1], 0, 0
charW, charH, chars = newFont and #ascii[1] / 16 or #ascii[1] * 3 / 64, #ascii / 16, {}
for yy = 0, newFont and 15 or 7 do
for xx = 0, 15 do
local newChar, length = {}, 0
-- Place in 2d grid of bools:
for y = 1, charH do
local newRow = {}
for x = 1, charW do
local set = ascii[y + ybump][x + xbump] == 1
if set and x > length then length = x end
newRow[x] = set
end
newChar[y] = newRow
end
-- Center:
if not newFont then for y = 1, charH do for x = 1, math.floor((charW - length) / 2) do table.insert(newChar[y], 1, false) end end end
chars[counter] = newChar
counter, xbump = counter + 1, xbump + (newFont and charW or charH)
end
xbump, ybump = 0, ybump + charH
end
end
snooze()
-- Terminal data translation:
do
local hex, counter = "0123456789abcdef", 1
for i = 1, 16 do
colourNum[counter] = hex:sub(i, i)
counter = counter * 2
end
end
for y = 1, ySize do
buffer[y] = {}
for x = 1, xSize do buffer[y][x] = {" ", colourNum[tCol], colourNum[bCol]} end
end
if showInput then for x = 1, xSize do buffer[ySize][x][3] = colourNum[colours.lightGrey] end end
tTerm.blit = function(text, fgCol, bgCol)
if xPos > xSize or xPos + #text - 1 < 1 or yPos < 1 or yPos > ySize then return end
if not _HOST then text = safeString(text) end
if not term.isColour() then
fgCol = safeCol(fgCol, "0")
bgCol = safeCol(bgCol, "f")
end
if xPos < 1 then
text = text:sub(2 - xPos)
fgCol = fgCol:sub(2 - xPos)
bgCol = bgCol:sub(2 - xPos)
xPos = 1
end
if xPos + #text - 1 > xSize then
text = text:sub(1, xSize - xPos + 1)
fgCol = fgCol:sub(1, xSize - xPos + 1)
bgCol = bgCol:sub(1, xSize - xPos + 1)
end
for x = 1, #text do
buffer[yPos][xPos + x - 1][1] = text:sub(x, x)
buffer[yPos][xPos + x - 1][2] = fgCol:sub(x, x)
buffer[yPos][xPos + x - 1][3] = bgCol:sub(x, x)
end
xPos = xPos + #text
end
tTerm.write = function(text)
text = tostring(text)
tTerm.blit(text, string.rep(colourNum[tCol], #text), string.rep(colourNum[bCol], #text))
end
tTerm.clearLine = function()
local oldXPos = xPos
xPos = 1
tTerm.write(string.rep(" ", xSize))
xPos = oldXPos
end
tTerm.clear = function()
local oldXPos, oldYPos = xPos, yPos
for y = 1, ySize do
xPos, yPos = 1, y
tTerm.write(string.rep(" ", xSize))
end
xPos, yPos = oldXPos, oldYPos
end
tTerm.setCursorPos = function(x, y)
xPos, yPos = math.floor(x), math.floor(y)
end
tTerm.setTextColour = function(col)
tCol = col
end
tTerm.setTextColor = function(col)
tCol = col
end
tTerm.setBackgroundColour = function(col)
bCol = col
end
tTerm.setBackgroundColor = function(col)
bCol = col
end
tTerm.scroll = function(lines)
if math.abs(lines) < ySize then
local oldXPos, oldYPos = xPos, yPos
for y = 1, ySize do
if y + lines > 0 and y + lines <= ySize then
for x = 1, xSize do
xPos, yPos = x, y
tTerm.blit(buffer[y + lines][x][1], buffer[y + lines][x][2], buffer[y + lines][x][3])
end
else
yPos = y
tTerm.clearLine()
end
end
xPos, yPos = oldXPos, oldYPos
else tTerm.clear() end
end
tTerm.toggleCur = function(newBlink)
curBlink = newBlink
end
tTerm.newInput = function(input)
local oldTC, oldBC, oldX, oldY = tCol, bCol, xPos, yPos
tCol, bCol, xPos, yPos, ySize, input = colours.grey, colours.lightGrey, 1, ySize + 1, ySize + 1, input .. " "
while #curInput + #input + 1 > xSize do curInput = curInput:sub(curInput:find(" ") + 1) end
curInput = curInput .. input .. " "
tTerm.clearLine()
tTerm.write(curInput)
tCol, bCol, xPos, yPos, ySize = oldTC, oldBC, oldX, oldY, ySize - 1
end
tTerm.key = function(key)
tTerm.newInput((not keys.getName(key)) and "unknownKey" or keys.getName(key))
end
tTerm.mouse_click = function(button, x, y)
tTerm.newInput(buttons[button] .. "C@" .. tostring(x) .. "x" .. tostring(y))
end
local image = {["width"] = xSize * charW, ["height"] = ySize * charH}
for i = 1, #calls do
local xMin, yMin, xMax, yMax, oldBuffer, curCalls, changed = xSize + 1, ySize + 1, 0, 0, {}, calls[i], false
calls[i] = nil
for y = 1, ySize do
oldBuffer[y] = {}
for x = 1, xSize do oldBuffer[y][x] = {buffer[y][x][1], buffer[y][x][2], buffer[y][x][3], buffer[y][x][4]} end
end
snooze()
if showInput then ySize = ySize - 1 end
for j = 1, #curCalls do if tTerm[curCalls[j][1]] then tTerm[curCalls[j][1]](unpack(curCalls[j], 2)) end end
if showInput then ySize = ySize + 1 end
if i > 1 then
for yy = 1, ySize do for xx = 1, xSize do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] then
changed = true
if xx < xMin then xMin = xx end
if xx > xMax then xMax = xx end
if yy < yMin then yMin = yy end
if yy > yMax then yMax = yy end
end end end
else xMin, yMin, xMax, yMax, changed = 1, 1, xSize, ySize, true end
if oldBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not curBlink) and oldXPos > 0 and oldYPos > 0 and oldXPos <= xSize and oldYPos <= ySize then
changed = true
if oldXPos < xMin then xMin = oldXPos end
if oldXPos > xMax then xMax = oldXPos end
if oldYPos < yMin then yMin = oldYPos end
if oldYPos > yMax then yMax = oldYPos end
buffer[oldYPos][oldXPos][4] = false
end
if curBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not oldBlink) and xPos > 0 and yPos > 0 and xPos <= xSize and yPos <= ySize then
changed = true
if xPos < xMin then xMin = xPos end
if xPos > xMax then xMax = xPos end
if yPos < yMin then yMin = yPos end
if yPos > yMax then yMax = yPos end
buffer[yPos][xPos][4] = true
end
oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos
local thisFrame = {
["xstart"] = (xMin - 1) * charW,
["ystart"] = (yMin - 1) * charH,
["xend"] = (xMax - xMin + 1) * charW,
["yend"] = (yMax - yMin + 1) * charH,
["delay"] = curCalls.delay,
["disposal"] = 1
}
for y = 1, (yMax - yMin + 1) * charH do
local row = {}
for x = 1, (xMax - xMin + 1) * charW do row[x] = " " end
thisFrame[y] = row
end
snooze()
for yy = yMin, yMax do
local yBump = (yy - yMin) * charH
for xx = xMin, xMax do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] or buffer[yy][xx][4] ~= oldBuffer[yy][xx][4] or i == 1 then
local thisChar, thisT, thisB, xBump = chars[buffer[yy][xx][1]:byte()], buffer[yy][xx][2], buffer[yy][xx][3], (xx - xMin) * charW
if thisChar then
for y = 1, charH do
for x = 1, charW do
local ch = thisChar[y][x] and thisT or thisB
thisFrame[y + yBump][x + xBump] = ch
end
end
end
if buffer[yy][xx][4] then
thisT, thisChar = colourNum[tCol], chars[95]
for y = 1, charH do for x = 1, charW do if thisChar[y][x] then thisFrame[y + yBump][x + xBump] = thisT end end end
end
end end
for y = yBump + 1, yBump + charH do
local skip, chars, row = 0, {}, {}
for x = 1, #thisFrame[y] do
if thisFrame[y][x] == " " then
if #chars > 0 then
row[#row + 1] = table.concat(chars)
chars = {}
end
skip = skip + 1
else
if skip > 0 then
row[#row + 1] = skip
skip = 0
end
chars[#chars + 1] = thisFrame[y][x]
end
end
if #chars > 0 then row[#row + 1] = table.concat(chars) end
thisFrame[y] = row
end
snooze()
end
if changed then
image[#image + 1] = thisFrame
else
image[#image].delay = image[#image].delay + curCalls.delay
end
end
buffer = nil
GIF.saveGIF(image, filename)
fs.unmount('.recGif')
print("Encode complete")

535
sys/apps/refinedManager.lua Normal file
View File

@@ -0,0 +1,535 @@
local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())()
require = injector(getfenv(1))
local UI = require('ui')
local RefinedProvider = require('refinedProvider')
local Terminal = require('terminal')
local Peripheral = require('peripheral')
local controller = RefinedProvider()
if not controller:isValid() then
error('Refined storage controller not found')
end
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
function getItem(items, inItem, ignoreDamage)
for _,item in pairs(items) do
if item.name == inItem.name then
if ignoreDamage then
return item
elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then
return item
end
end
end
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
function mergeResources(t)
local resources = Util.readTable('resource.limits') or { }
for _,v in pairs(resources) do
v.low = tonumber(v.low) -- backwards compatibility
local item = getItem(t, v)
if item then
item.low = v.low
item.auto = v.auto
item.ignoreDamage = v.ignoreDamage
item.rsControl = v.rsControl
item.rsDevice = v.rsDevice
item.rsSide = v.rsSide
else
v.count = 0
table.insert(t, v)
end
end
for _,v in pairs(t) do
v.lname = v.displayName:lower()
end
end
function filterItems(t, filter)
if filter then
local r = {}
filter = filter:lower()
for k,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
return r
end
return t
end
function craftItems(itemList, allItems)
for _,item in pairs(itemList) do
local cItem = getItem(allItems, item)
if controller:isCrafting(item) then
item.status = '(crafting)'
elseif item.rsControl then
item.status = 'Activated'
elseif not cItem then
item.status = '(no recipe)'
else
local count = item.count
while count >= 1 do -- try to request smaller quantities until successful
local s, m = pcall(function()
item.status = '(no recipe)'
if not controller:craft(cItem, count) then
item.status = '(missing ingredients)'
error('failed')
end
item.status = '(crafting)'
end)
if s then
break -- successfully requested crafting
end
count = math.floor(count / 2)
end
end
end
end
function getAutocraftItems()
local t = Util.readTable('resource.limits') or { }
local itemList = { }
for _,res in pairs(t) do
if res.auto then
res.count = 4 -- this could be higher to increase autocrafting speed
table.insert(itemList, res)
end
end
return itemList
end
local function getItemWithQty(items, res, ignoreDamage)
local item = getItem(items, res, ignoreDamage)
if item then
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
end
end
item.count = count
end
end
return item
end
function watchResources(items)
local itemList = { }
local t = Util.readTable('resource.limits') or { }
for k, res in pairs(t) do
res.low = tonumber(res.low) -- backwards compatibility
local item = getItemWithQty(items, res, res.ignoreDamage)
if not item then
item = {
damage = res.damage,
nbtHash = res.nbtHash,
name = res.name,
displayName = res.displayName,
count = 0
}
end
if res.low and item.count < res.low then
if res.ignoreDamage then
item.damage = 0
end
table.insert(itemList, {
damage = item.damage,
nbtHash = item.nbtHash,
count = res.low - item.count,
name = item.name,
displayName = item.displayName,
status = '',
rsControl = res.rsControl,
})
end
if res.rsControl and res.rsDevice and res.rsSide then
pcall(function()
device[res.rsDevice].setOutput(res.rsSide, item.count < res.low)
end)
end
end
return itemList
end
itemPage = UI.Page {
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green
},
displayName = UI.Window {
x = 5, y = 3, width = UI.term.width - 10, height = 3,
},
form = UI.Form {
x = 4, y = 6, height = 10, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Craft until out of ingredients'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore damage of item'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'RS Control', formKey = 'rsControl',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Control via redstone'
},
[5] = UI.Chooser {
width = 25,
formLabel = 'RS Device', formKey = 'rsDevice',
--choices = devices,
help = 'Redstone Device'
},
[6] = UI.Chooser {
width = 10,
formLabel = 'RS Side', formKey = 'rsSide',
--nochoice = 'No',
choices = {
{ name = 'up', value = 'up' },
{ name = 'down', value = 'down' },
{ name = 'east', value = 'east' },
{ name = 'north', value = 'north' },
{ name = 'west', value = 'west' },
{ name = 'south', value = 'south' },
},
help = 'Output side'
},
},
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('\nNBT: %s\n', 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
local devices = self.form[5].choices
Util.clear(devices)
for _,device in pairs(device) do
if device.setOutput then
table.insert(devices, { name = device.name, value = device.name })
end
end
if Util.size(devices) == 0 then
table.insert(devices, { name = 'None found', values = '' })
end
UI.Page.enable(self)
self:focusFirst()
end
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
self.statusBar:draw()
elseif event.type == 'form_complete' then
local values = self.form.values
local t = Util.readTable('resource.limits') or { }
local keys = { 'name', 'displayName', 'auto', 'low', 'damage',
'maxDamage', 'nbtHash', 'ignoreDamage',
'rsControl', 'rsDevice', 'rsSide', }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
filtered.low = tonumber(filtered.low)
filtered.ignoreDamage = filtered.ignoreDamage == true
filtered.auto = filtered.auto == true
filtered.rsControl = filtered.rsControl == true
if filtered.ignoreDamage then
filtered.damage = 0
end
t[uniqueKey(filtered)] = filtered
Util.writeTable('resource.limits', t)
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Forget', event = 'forget' },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'displayName', width = UI.term.width - 14 },
{ heading = 'Qty', key = 'count', width = 5 },
{ heading = 'Min', key = 'low', width = 4 },
},
sortColumn = 'lname',
},
statusBar = UI.StatusBar {
backgroundColor = colors.gray,
width = UI.term.width,
filterText = UI.Text {
x = 2, width = 6,
value = 'Filter',
},
filter = UI.TextEntry {
x = 9, rex = -12,
limit = 50,
},
refresh = UI.Button {
rx = -9, width = 8,
text = 'Refresh',
event = 'refresh',
},
},
accelerators = {
r = 'refresh',
q = 'quit',
}
}
function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then -- not implemented
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count)
if row.low then
row.low = Util.toBytes(row.low)
end
return row
end
function listingPage.statusBar:draw()
return UI.Window.draw(self)
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
UI:setPage('item', selected)
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
self.statusBar.filter:focus()
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local resources = Util.readTable('resource.limits') or { }
resources[uniqueKey(item)] = nil
Util.writeTable('resource.limits', resources)
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
self:refresh()
self.grid:draw()
end
elseif event.type == 'text_change' then
self.filter = event.text
if #self.filter == 0 then
self.filter = nil
end
self:applyFilter()
self.grid:draw()
self.statusBar.filter:focus()
else
UI.Page.eventHandler(self, event)
end
return true
end
function listingPage:enable()
self:refresh()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
end
function listingPage:refresh()
self.allItems = controller:listItems()
mergeResources(self.allItems)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
local function jobMonitor(jobList)
local mon = Peripheral.getByType('monitor')
if mon then
mon = UI.Device({
device = device.monitor,
textScale = .5,
})
else
mon = UI.Device({
device = Terminal.getNullTerm(term.current())
})
end
jobListGrid = UI.Grid {
parent = mon,
sortColumn = 'displayName',
columns = {
{ heading = 'Qty', key = 'count', width = 6 },
{ heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 },
{ heading = 'Status', key = 'status', width = mon.width - 10 },
},
}
return jobListGrid
end
UI:setPages({
listing = listingPage,
item = itemPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
local jobListGrid = jobMonitor()
jobListGrid:draw()
jobListGrid:sync()
function craftingThread()
while true do
os.sleep(5)
--pcall(function()
local items = controller:listItems()
if not controller:isOnline() then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
jobListGrid:sync()
elseif Util.size(items) == 0 then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
jobListGrid:sync()
else
local itemList = watchResources(items)
jobListGrid:setValues(itemList)
jobListGrid:draw()
jobListGrid:sync()
craftItems(itemList, items)
--jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
itemList = getAutocraftItems() -- autocrafted items don't show on job monitor
craftItems(itemList, items)
end
--end)
end
end
UI:pullEvents(craftingThread)
UI.term:reset()
jobListGrid.parent:reset()

1
sys/apps/scripts/abort Normal file
View File

@@ -0,0 +1 @@
turtle.abortAction()

121
sys/apps/scripts/follow Normal file
View File

@@ -0,0 +1,121 @@
local function follow(id)
require = requireInjector(getfenv(1))
local GPS = require('gps')
local Socket = require('socket')
local Point = require('point')
local process = require('process')
turtle.status = 'follow ' .. id
local pt = GPS.getPointAndHeading()
if not pt or not pt.heading then
error('turtle: No GPS found')
end
turtle.setPoint(pt)
local socket = Socket.connect(id, 161)
if not socket then
error('turtle: Unable to connect to ' .. id)
return
end
local lastPoint
local following = false
local followThread = process:newThread('follower', function()
while true do
local function getRemotePoint()
if not turtle.abort then
if socket:write({ type = 'gps' }) then
return socket:read(3)
end
end
end
-- sometimes gps will fail if moving
local pt, d
for i = 1, 3 do
pt, d = getRemotePoint()
if pt then
break
end
os.sleep(.5)
end
if not pt or turtle.abort then
error('Did not receive GPS location')
end
if not lastPoint or (lastPoint.x ~= pt.x or lastPoint.y ~= pt.y or lastPoint.z ~= pt.z) then
if following then
turtle.abort = true
while following do
os.sleep(.1)
end
turtle.abort = false
end
-- check if gps is inaccurate (player moving too fast)
if d < Point.pythagoreanDistance(turtle.point, pt) + 10 then
lastPoint = Point.copy(pt)
following = true
process:newThread('turtle_follow', function()
local pts = {
{ x = pt.x + 2, z = pt.z, y = pt.y },
{ x = pt.x - 2, z = pt.z, y = pt.y },
{ x = pt.x, z = pt.z + 2, y = pt.y },
{ x = pt.x, z = pt.z - 2, y = pt.y },
}
local cpt = Point.closest(turtle.point, pts)
local blocks = { }
local function addBlocks(tpt)
table.insert(blocks, tpt)
local apts = Point.adjacentPoints(tpt)
for _,apt in pairs(apts) do
table.insert(blocks, apt)
end
end
-- don't run into player
addBlocks(pt)
addBlocks({ x = pt.x, z = pt.z, y = pt.y + 1 })
if turtle.pathfind(cpt, blocks) then
turtle.headTowards(pt)
end
following = false
end)
end
end
os.sleep(.5)
end
end)
while true do
local e = process:pullEvent()
if e == 'terminate' or followThread:isDead() or e =='turtle_abort' then
process:threadEvent('terminate')
break
end
end
socket:close()
return true
end
local s, m = turtle.run(function() follow({COMPUTER_ID}) end)
if not s and m then
error(m)
end

1
sys/apps/scripts/goHome Normal file
View File

@@ -0,0 +1 @@
turtle.run(turtle.gotoGPSHome)

30
sys/apps/scripts/moveTo Normal file
View File

@@ -0,0 +1,30 @@
turtle.run(function()
require = requireInjector(getfenv(1))
local GPS = require('gps')
local Socket = require('socket')
local id = {COMPUTER_ID}
local pt = GPS.getPointAndHeading()
if not pt or not pt.heading then
error('turtle: No GPS found')
end
turtle.setPoint(pt)
local socket = Socket.connect(id, 161)
if not socket then
error('turtle: Unable to connect to ' .. id)
end
socket:write({ type = 'gps' })
local pt = socket:read(3)
if not pt then
error('turtle: No GPS response')
end
if not turtle.pathfind(pt, nil, 64) then
error('Unable to go to location')
end
end)

100
sys/apps/scripts/obsidian Normal file
View File

@@ -0,0 +1,100 @@
require = requireInjector(getfenv(1))
local Point = require('point')
local checkedNodes, nodes
local function addNode(node)
for i = 0, 3 do
local hi = turtle.getHeadingInfo(i)
local testNode = { x = node.x + hi.xd, z = node.z + hi.zd }
local key = table.concat({ testNode.x, testNode.z }, ':')
if not checkedNodes[key] then
nodes[key] = testNode
end
end
end
local function findObsidian()
repeat
local node = { x = turtle.point.x, z = turtle.point.z }
local key = table.concat({ node.x, node.z }, ':')
checkedNodes[key] = true
nodes[key] = nil
local _,b = turtle.inspectDown()
if b and (b.name == 'minecraft:lava' or b.name == 'minecraft:flowing_lava') then
if turtle.selectSlot('minecraft:water_bucket') then
while true do
if turtle.up() then
break
end
print('stuck')
end
turtle.placeDown()
os.sleep(2)
turtle.placeDown()
turtle.down()
turtle.select(1)
_, b = turtle.inspectDown()
end
end
if turtle.getCount(16) > 0 then
print('Inventory full')
print('Enter to continue...')
read()
end
if b and b.name == 'minecraft:obsidian' then
turtle.digDown()
addNode(node)
else
turtle.digDown()
end
print(string.format('%d nodes remaining', Util.size(nodes)))
if Util.size(nodes) == 0 then
break
end
local node = Point.closest(turtle.point, nodes)
if not turtle.gotoPoint(node) then
break
end
until turtle.abort
end
turtle.reset()
turtle.setPolicy(turtle.policies.digOnly)
local s, m = turtle.run(function()
repeat
checkedNodes = { }
nodes = { }
local _,b = turtle.inspectDown()
if not b or b.name ~= 'minecraft:obsidian' then
break
end
findObsidian()
if not turtle.selectSlot('minecraft:water_bucket') then
break
end
turtle.goto(0, 0)
turtle.placeDown()
os.sleep(2)
turtle.placeDown()
turtle.down()
turtle.select(1)
until turtle.abort
end)
turtle.goto(0, 0, 0, 0)
turtle.reset()
if not s and m then
error(m)
end

1
sys/apps/scripts/reboot Normal file
View File

@@ -0,0 +1 @@
os.reboot()

1
sys/apps/scripts/setHome Normal file
View File

@@ -0,0 +1 @@
turtle.run(turtle.setGPSHome)

View File

@@ -0,0 +1 @@
os.shutdown()

73
sys/apps/scripts/summon Normal file
View File

@@ -0,0 +1,73 @@
local function summon(id)
require = requireInjector(getfenv(1))
local GPS = require('gps')
local Socket = require('socket')
local Point = require('point')
turtle.status = 'GPSing'
turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0 })
local pts = {
[ 1 ] = { x = 0, z = 0, y = 0 },
[ 2 ] = { x = 4, z = 0, y = 0 },
[ 3 ] = { x = 2, z = -2, y = 2 },
[ 4 ] = { x = 2, z = 2, y = 2 },
}
local tFixes = { }
local socket = Socket.connect(id, 161)
if not socket then
error('turtle: Unable to connect to ' .. id)
end
local function getDistance()
socket:write({ type = 'ping' })
local _, d = socket:read(5)
return d
end
local function doGPS()
tFixes = { }
for i = 1, 4 do
if not turtle.gotoPoint(pts[i]) then
error('turtle: Unable to perform GPS maneuver')
end
local distance = getDistance()
if not distance then
error('turtle: No response from ' .. id)
end
table.insert(tFixes, {
position = vector.new(turtle.point.x, turtle.point.y, turtle.point.z),
distance = distance
})
end
return true
end
if not doGPS() then
turtle.turnAround()
turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0})
if not doGPS() then
socket:close()
return false
end
end
socket:close()
local pos = GPS.trilaterate(tFixes)
if pos then
local pt = { x = pos.x, y = pos.y, z = pos.z }
local _, h = Point.calculateMoves(turtle.getPoint(), pt)
local hi = turtle.getHeadingInfo(h)
turtle.status = 'recalling'
turtle.pathfind({ x = pt.x - hi.xd, z = pt.z - hi.zd, y = pt.y - hi.yd, heading = h })
else
error("turtle: Could not determine position")
end
end
turtle.run(function() summon({COMPUTER_ID}) end)

1
sys/apps/scripts/update Normal file
View File

@@ -0,0 +1 @@
shell.run('/apps/update.lua')

623
sys/apps/shell Normal file
View File

@@ -0,0 +1,623 @@
local parentShell = shell
shell = { }
local sandboxEnv = Util.shallowCopy(getfenv(1))
setmetatable(sandboxEnv, { __index = _G })
local DIR = (parentShell and parentShell.dir()) or ""
local PATH = (parentShell and parentShell.path()) or ".:/rom/programs"
local ALIASES = (parentShell and parentShell.aliases()) or {}
local tCompletionInfo = (parentShell and parentShell.getCompletionInfo()) or {}
local bExit = false
local tProgramStack = {}
local function parseCommandLine( ... )
local sLine = table.concat( { ... }, " " )
local tWords = {}
local bQuoted = false
for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do
if bQuoted then
table.insert( tWords, match )
else
for m in string.gmatch( match, "[^ \t]+" ) do
table.insert( tWords, m )
end
end
bQuoted = not bQuoted
end
return table.remove(tWords, 1), tWords
end
-- Install shell API
function shell.run(...)
local path, args = parseCommandLine(...)
path = shell.resolveProgram(path)
if path then
tProgramStack[#tProgramStack + 1] = path
local oldTitle
if multishell and multishell.getTitle then
oldTitle = multishell.getTitle(multishell.getCurrent())
multishell.setTitle(multishell.getCurrent(), fs.getName(path))
end
local result, err = os.run(Util.shallowCopy(sandboxEnv), path, unpack(args))
if multishell then
local title = 'shell'
if #tProgramStack > 0 then
title = fs.getName(tProgramStack[#tProgramStack])
end
multishell.setTitle(multishell.getCurrent(), oldTitle or 'shell')
end
return result, err
end
return false, 'No such program'
end
function shell.exit()
bExit = true
end
function shell.dir() return DIR end
function shell.setDir(d) DIR = d end
function shell.path() return PATH end
function shell.setPath(p) PATH = p end
function shell.resolve( _sPath )
local sStartChar = string.sub( _sPath, 1, 1 )
if sStartChar == "/" or sStartChar == "\\" then
return fs.combine( "", _sPath )
else
return fs.combine(DIR, _sPath )
end
end
function shell.resolveProgram( _sCommand )
local sPath = PATH or ''
if ALIASES[ _sCommand ] ~= nil then
_sCommand = ALIASES[ _sCommand ]
end
local path = shell.resolve(_sCommand)
if fs.exists(path) and not fs.isDir(path) then
return path
end
if fs.exists(path .. '.lua') then
return path .. '.lua'
end
-- If the path is a global path, use it directly
local sStartChar = string.sub( _sCommand, 1, 1 )
if sStartChar == "/" or sStartChar == "\\" then
local sPath = fs.combine( "", _sCommand )
if fs.exists( sPath ) and not fs.isDir( sPath ) then
return sPath
end
return nil
end
-- Otherwise, look on the path variable
for sPath in string.gmatch(sPath, "[^:]+") do
sPath = fs.combine( shell.resolve(sPath), _sCommand )
if fs.exists( sPath ) and not fs.isDir( sPath ) then
return sPath
end
if fs.exists(sPath .. '.lua') then
return sPath .. '.lua'
end
end
-- Not found
return nil
end
function shell.programs( _bIncludeHidden )
local tItems = {}
-- Add programs from the path
for sPath in string.gmatch(PATH, "[^:]+") do
sPath = shell.resolve(sPath)
if fs.isDir( sPath ) then
local tList = fs.list( sPath )
for n,sFile in pairs( tList ) do
if not fs.isDir( fs.combine( sPath, sFile ) ) and
(_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then
tItems[ sFile ] = true
end
end
end
end
-- Sort and return
local tItemList = {}
for sItem, b in pairs( tItems ) do
table.insert( tItemList, sItem )
end
table.sort( tItemList )
return tItemList
end
function shell.complete(sLine) end
function shell.completeProgram(sProgram) end
function shell.setCompletionFunction(sProgram, fnComplete)
tCompletionInfo[sProgram] = { fnComplete = fnComplete }
end
function shell.getCompletionInfo()
return tCompletionInfo
end
function shell.getRunningProgram()
return tProgramStack[#tProgramStack]
end
function shell.set(name, value)
getfenv(1)[name] = value
end
function shell.get(name)
return getfenv(1)[name]
end
function shell.setAlias( _sCommand, _sProgram )
ALIASES[ _sCommand ] = _sProgram
end
function shell.clearAlias( _sCommand )
ALIASES[ _sCommand ] = nil
end
function shell.aliases()
local tCopy = {}
for sAlias, sCommand in pairs(ALIASES) do
tCopy[sAlias] = sCommand
end
return tCopy
end
function shell.newTab(tabInfo, ...)
local path, args = parseCommandLine(...)
path = shell.resolveProgram(path)
if path then
tabInfo.path = path
tabInfo.env = sandboxEnv
tabInfo.args = args
tabInfo.title = fs.getName(path)
return multishell.openTab(tabInfo)
end
return nil, 'No such program'
end
function shell.openTab( ... )
return shell.newTab({ }, ...)
end
function shell.openForegroundTab( ... )
return shell.newTab({ focused = true }, ...)
end
function shell.openHiddenTab( ... )
return shell.newTab({ hidden = true }, ...)
end
function shell.switchTab(tabId)
multishell.setFocus(tabId)
end
local tArgs = { ... }
if #tArgs > 0 then
-- "shell x y z"
-- Run the program specified in this new shell
local s, m = shell.run( ... )
if not s and m ~= 'Terminated' then
error(m or '')
end
return s, m
end
require = requireInjector(getfenv(1))
local Config = require('config')
local History = require('history')
local config = {
standard = {
textColor = colors.white,
commandTextColor = colors.lightGray,
directoryTextColor = colors.gray,
directoryBackgroundColor = colors.black,
promptTextColor = colors.gray,
promptBackgroundColor = colors.black,
directoryColor = colors.gray,
},
color = {
textColor = colors.white,
commandTextColor = colors.yellow,
directoryTextColor = colors.orange,
directoryBackgroundColor = colors.black,
promptTextColor = colors.blue,
promptBackgroundColor = colors.black,
directoryColor = colors.green,
},
displayDirectory = true,
}
--Config.load('shell', config)
local _colors = config.standard
if term.isColor() then
_colors = config.color
end
local function autocompleteFile(results, words)
local function getBaseDir(path)
if #path > 1 then
if path:sub(-1) ~= '/' then
path = fs.getDir(path)
end
end
if path:sub(1, 1) == '/' then
path = fs.combine(path, '')
else
path = fs.combine(shell.dir(), path)
end
while not fs.isDir(path) do
path = fs.getDir(path)
end
return path
end
local function getRawPath(path)
local baseDir = ''
if path:sub(1, 1) ~= '/' then
baseDir = shell.dir()
end
if #path > 1 then
if path:sub(-1) ~= '/' then
path = fs.getDir(path)
end
end
if fs.isDir(fs.combine(baseDir, path)) then
return path
end
return fs.getDir(path)
end
local match = words[#words] or ''
local startDir = getBaseDir(match)
local rawPath = getRawPath(match)
if fs.isDir(startDir) then
local files = fs.list(startDir)
debug({ rawPath, startDir })
for _,f in pairs(files) do
local path = fs.combine(rawPath, f)
if fs.isDir(fs.combine(startDir, f)) then
results[path .. '/'] = 'directory'
else
results[path .. ' '] = 'program'
end
end
end
end
local function autocompleteProgram(results, words)
if #words == 1 then
local files = shell.programs(true)
for _,f in ipairs(files) do
results[f .. ' '] = 'program'
end
for f in pairs(ALIASES) do
results[f .. ' '] = 'program'
end
end
end
local function autocompleteArgument(results, program, words)
local word = ''
if #words > 1 then
word = words[#words]
end
local tInfo = tCompletionInfo[program]
local args = tInfo.fnComplete(shell, #words - 1, word, words)
if args then
Util.filterInplace(args, function(f)
return not Util.key(args, f .. '/')
end)
for _,arg in ipairs(args) do
results[word .. arg] = 'argument'
end
end
end
local function autocomplete(line, suggestions)
local words = { }
for word in line:gmatch("%S+") do
table.insert(words, word)
end
if line:match(' $') then
table.insert(words, '')
end
local results = { }
if #words == 0 then
files = autocompleteFile(results, words)
else
local program = shell.resolveProgram(words[1])
if tCompletionInfo[program] then
autocompleteArgument(results, program, words)
else
autocompleteProgram(results, words)
autocompleteFile(results, words)
end
end
local match = words[#words] or ''
local files = { }
for f in pairs(results) do
if f:sub(1, #match) == match then
table.insert(files, f)
end
end
if #files == 1 then
words[#words] = files[1]
return table.concat(words, ' ')
elseif #files > 1 and suggestions then
print()
local word = words[#words] or ''
local prefix = word:match("(.*/)") or ''
if #prefix > 0 then
for _,f in ipairs(files) do
if f:match("^" .. prefix) ~= prefix then
prefix = ''
break
end
end
end
local tDirs, tFiles = { }, { }
for _,f in ipairs(files) do
if results[f] == 'directory' then
f = f:gsub(prefix, '', 1)
table.insert(tDirs, f)
else
f = f:gsub(prefix, '', 1)
table.insert(tFiles, f)
end
end
table.sort(tDirs)
table.sort(tFiles)
if #tDirs > 0 and #tDirs < #tFiles then
local w = term.getSize()
local nMaxLen = w / 8
for n, sItem in pairs(files) do
nMaxLen = math.max(string.len(sItem) + 1, nMaxLen)
end
local nCols = math.floor(w / nMaxLen)
if #tDirs < nCols then
for i = #tDirs + 1, nCols do
table.insert(tDirs, '')
end
end
end
if #tDirs > 0 then
textutils.tabulate(_colors.directoryColor, tDirs, colors.white, tFiles)
else
textutils.tabulate(colors.white, tFiles)
end
term.setTextColour(_colors.promptTextColor)
term.setBackgroundColor(_colors.promptBackgroundColor)
write("$ " )
term.setTextColour(_colors.commandTextColor)
term.setBackgroundColor(colors.black)
return line
elseif #files > 1 then
-- ugly (complete as much as possible)
local word = words[#words] or ''
local i = #word + 1
while true do
local ch
for _,f in ipairs(files) do
if #f < i then
words[#words] = string.sub(f, 1, i - 1)
return table.concat(words, ' ')
end
if not ch then
ch = string.sub(f, i, i)
elseif string.sub(f, i, i) ~= ch then
if i == #word + 1 then
return
end
words[#words] = string.sub(f, 1, i - 1)
return table.concat(words, ' ')
end
end
i = i + 1
end
end
end
local function shellRead(_tHistory )
term.setCursorBlink( true )
local sLine = ""
local nHistoryPos
local nPos = 0
local lastPattern
local w = term.getSize()
local sx = term.getCursorPos()
local function redraw( sReplace )
local nScroll = 0
if sx + nPos >= w then
nScroll = (sx + nPos) - w
end
local cx,cy = term.getCursorPos()
term.setCursorPos( sx, cy )
if sReplace then
term.write( string.rep( sReplace, math.max( string.len(sLine) - nScroll, 0 ) ) )
else
term.write( string.sub( sLine, nScroll + 1 ) )
end
term.setCursorPos( sx + nPos - nScroll, cy )
end
while true do
local sEvent, param, param2 = os.pullEventRaw()
if sEvent == "char" then
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
nPos = nPos + 1
redraw()
elseif sEvent == "paste" then
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
nPos = nPos + string.len( param )
redraw()
elseif sEvent == 'mouse_click' and param == 2 then
redraw(string.rep(' ', #sLine))
sLine = ''
nPos = 0
redraw()
elseif sEvent == 'terminate' then
bExit = true
break
elseif sEvent == "key" then
if param == keys.enter then
-- Enter
break
elseif param == keys.tab then
if nPos == #sLine then
local showSuggestions = lastPattern == sLine
lastPattern = sLine
local cline = autocomplete(sLine, showSuggestions)
if cline then
sLine = cline
nPos = #sLine
redraw()
end
end
elseif param == keys.left then
if nPos > 0 then
nPos = nPos - 1
redraw()
end
elseif param == keys.right then
if nPos < string.len(sLine) then
redraw(" ")
nPos = nPos + 1
redraw()
end
elseif param == keys.up or param == keys.down then
if _tHistory then
redraw(" ")
if param == keys.up then
if nHistoryPos == nil then
if #_tHistory > 0 then
nHistoryPos = #_tHistory
end
elseif nHistoryPos > 1 then
nHistoryPos = nHistoryPos - 1
end
else
if nHistoryPos == #_tHistory then
nHistoryPos = nil
elseif nHistoryPos ~= nil then
nHistoryPos = nHistoryPos + 1
end
end
if nHistoryPos then
sLine = _tHistory[nHistoryPos]
nPos = string.len( sLine )
else
sLine = ""
nPos = 0
end
redraw()
end
elseif param == keys.backspace then
if nPos > 0 then
redraw(" ")
sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
nPos = nPos - 1
redraw()
end
elseif param == keys.home then
redraw(" ")
nPos = 0
redraw()
elseif param == keys.delete then
if nPos < string.len(sLine) then
redraw(" ")
sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
redraw()
end
elseif param == keys["end"] then
redraw(" ")
nPos = string.len(sLine)
redraw()
end
elseif sEvent == "term_resize" then
w = term.getSize()
redraw()
end
end
local cx, cy = term.getCursorPos()
term.setCursorPos( w + 1, cy )
print()
term.setCursorBlink( false )
return sLine
end
local history = History.load('usr/.shell_history', 25)
while not bExit do
if config.displayDirectory then
term.setTextColour(_colors.directoryTextColor)
term.setBackgroundColor(_colors.directoryBackgroundColor)
print('==' .. os.getComputerLabel() .. ':/' .. DIR)
end
term.setTextColour(_colors.promptTextColor)
term.setBackgroundColor(_colors.promptBackgroundColor)
write("$ " )
term.setTextColour(_colors.commandTextColor)
term.setBackgroundColor(colors.black)
local sLine = shellRead(history.entries)
if bExit then -- terminated
break
end
sLine = Util.trim(sLine)
if #sLine > 0 and sLine ~= 'exit' then
history.add(sLine)
end
term.setTextColour(_colors.textColor)
if #sLine > 0 then
local result, err = shell.run( sLine )
if not result then
printError(err)
end
end
end

631
sys/apps/simpleMiner.lua Normal file
View File

@@ -0,0 +1,631 @@
require = requireInjector(getfenv(1))
local Point = require('point')
local Logger = require('logger')
if device and device.wireless_modem then
Logger.setWirelessLogging()
end
local args = { ... }
local options = {
chunks = { arg = 'c', type = 'number', value = -1,
desc = 'Number of chunks to mine' },
depth = { arg = 'd', type = 'number', value = 9000,
desc = 'Mining depth' },
-- enderChest = { arg = 'e', type = 'flag', value = false,
-- desc = 'Use ender chest' },
resume = { arg = 'r', type = 'flag', value = false,
desc = 'Resume mining' },
fortunePick = { arg = 'p', type = 'string', value = nil,
desc = 'Pick to use with CCTweaks toolhost' },
setTrash = { arg = 's', type = 'flag', value = false,
desc = 'Set trash items' },
help = { arg = 'h', type = 'flag', value = false,
desc = 'Displays the options' },
}
local fortuneBlocks = {
[ 'minecraft:redstone_ore' ] = true,
[ 'minecraft:lapis_ore' ] = true,
[ 'minecraft:coal_ore' ] = true,
[ 'minecraft:diamond_ore' ] = true,
[ 'minecraft:emerald_ore' ] = true,
}
local MIN_FUEL = 7500
local LOW_FUEL = 1500
local MAX_FUEL = 100000
if not term.isColor() then
MAX_FUEL = 20000
end
local mining = {
diameter = 1,
chunkIndex = 0,
chunks = -1,
}
local trash
local boreDirection
function getChunkCoordinates(diameter, index, x, z)
local dirs = { -- circumference of grid
{ xd = 0, zd = 1, heading = 1 }, -- south
{ xd = -1, zd = 0, heading = 2 },
{ xd = 0, zd = -1, heading = 3 },
{ xd = 1, zd = 0, heading = 0 } -- east
}
-- always move east when entering the next diameter
if index == 0 then
dirs[4].x = x + 16
dirs[4].z = z
return dirs[4]
end
dir = dirs[math.floor(index / (diameter - 1)) + 1]
dir.x = x + dir.xd * 16
dir.z = z + dir.zd * 16
return dir
end
function getBoreLocations(x, z)
local locations = {}
while true do
local a = math.abs(z)
local b = math.abs(x)
if x > 0 and z > 0 or
x < 0 and z < 0 then
-- rotate coords
a = math.abs(x)
b = math.abs(z)
end
if (a % 5 == 0 and b % 5 == 0) or
(a % 5 == 2 and b % 5 == 1) or
(a % 5 == 4 and b % 5 == 2) or
(a % 5 == 1 and b % 5 == 3) or
(a % 5 == 3 and b % 5 == 4) then
table.insert(locations, { x = x, z = z, y = 0 })
end
if z % 2 == 0 then -- forward dir
if (x + 1) % 16 == 0 then
z = z + 1
else
x = x + 1
end
else
if (x - 1) % 16 == 15 then
if (z + 1) % 16 == 0 then
break
end
z = z + 1
else
x = x - 1
end
end
end
return locations
end
-- get the bore location closest to the miner
local function getClosestLocation(points, b)
local key = 1
local leastMoves = 9000
for k,pt in pairs(points) do
local moves = Point.calculateMoves(turtle.point, pt)
if moves < leastMoves then
key = k
leastMoves = moves
if leastMoves == 0 then
break
end
end
end
return table.remove(points, key)
end
function getCornerOf(c)
return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16
end
function nextChunk()
local x, z = getCornerOf({ x = mining.x, z = mining.z })
local points = math.pow(mining.diameter, 2) - math.pow(mining.diameter-2, 2)
mining.chunkIndex = mining.chunkIndex + 1
if mining.chunkIndex >= points then
mining.diameter = mining.diameter + 2
mining.chunkIndex = 0
end
if mining.chunks ~= -1 then
local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex
if chunks >= mining.chunks then
return false
end
end
local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z)
mining.locations = getBoreLocations(nc.x, nc.z)
-- enter next chunk
mining.x = nc.x
mining.z = nc.z
Util.writeTable('mining.progress', mining)
return true
end
function addTrash()
if not trash then
trash = { }
end
local slots = turtle.getFilledSlots()
for k,slot in pairs(slots) do
trash[slot.iddmg] = true
end
trash['minecraft:bucket:0'] = nil
Util.writeTable('mining.trash', trash)
end
function log(text)
print(text)
Logger.log('mineWorker', text)
end
function status(status)
turtle.status = status
log(status)
end
function refuel()
if turtle.getFuelLevel() < MIN_FUEL then
local oldStatus = turtle.status
status('refueling')
if turtle.selectSlot('minecraft:coal:0') then
local qty = turtle.getItemCount()
print('refueling ' .. qty)
turtle.refuel(qty)
end
if turtle.getFuelLevel() < MIN_FUEL then
log('desperate fueling')
turtle.eachFilledSlot(function(slot)
if turtle.getFuelLevel() < MIN_FUEL then
turtle.select(slot.index)
turtle.refuel(64)
end
end)
end
log('Fuel: ' .. turtle.getFuelLevel())
status(oldStatus)
end
turtle.select(1)
end
function enderChestUnload()
log('unloading')
turtle.select(1)
if not Util.tryTimed(5, function()
turtle.digDown()
return turtle.placeDown()
end) then
log('placedown failed')
else
turtle.reconcileInventory(slots, turtle.dropDown)
turtle.select(1)
turtle.drop(64)
turtle.digDown()
end
end
function safeGoto(x, z, y, h)
local oldStatus = turtle.status
while not turtle.pathfind({ x = x, z = z, y = y, heading = h }) do
--status('stuck')
if turtle.abort then
return false
end
--os.sleep(1)
end
turtle.status = oldStatus
return true
end
function safeGotoY(y)
local oldStatus = turtle.status
while not turtle.gotoY(y) do
status('stuck')
if turtle.abort then
return false
end
os.sleep(1)
end
turtle.status = oldStatus
return true
end
function makeWalkableTunnel(action, tpt, pt)
if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source
if not Point.compare(tpt, pt) then -- not at dest
local r, block = turtle.inspectUp()
if r and not turtle.isTurtleAtSide('top') then
if block.name ~= 'minecraft:cobblestone' and
block.name ~= 'minecraft:chest' then
turtle.digUp()
end
end
end
end
end
function normalChestUnload()
local oldStatus = turtle.status
status('unloading')
local pt = Util.shallowCopy(turtle.point)
safeGotoY(0)
turtle.setMoveCallback(function(action, tpt)
makeWalkableTunnel(action, tpt, { x = pt.x, z = pt.z })
end)
safeGoto(0, 0)
if not turtle.detectUp() then
error('no chest')
end
local slots = turtle.getFilledSlots()
for _,slot in pairs(slots) do
if not trash[slot.iddmg] and
slot.iddmg ~= 'minecraft:bucket:0' and
slot.id ~= 'minecraft:diamond_pickaxe' and
slot.id ~= 'cctweaks:toolHost' then
if slot.id ~= options.fortunePick.value then
turtle.select(slot.index)
turtle.dropUp(64)
end
end
end
turtle.select(1)
safeGoto(pt.x, pt.z, 0, pt.heading)
turtle.clearMoveCallback()
safeGotoY(pt.y)
status(oldStatus)
end
function ejectTrash()
local cobbleSlotCount = 0
turtle.eachFilledSlot(function(slot)
if slot.iddmg == 'minecraft:cobblestone:0' then
cobbleSlotCount = cobbleSlotCount + 1
end
if trash[slot.iddmg] then
-- retain 1 slot with cobble in order to indicate active mining
if slot.iddmg ~= 'minecraft:cobblestone:0' or cobbleSlotCount > 1 then
turtle.select(slot.index)
turtle.dropDown(64)
end
end
end)
end
function mineable(action)
local r, block = action.inspect()
if not r then
return false
end
if block.name == 'minecraft:chest' then
collectDrops(action.suck)
end
if turtle.getFuelLevel() < (MAX_FUEL - 1000) then
if block.name == 'minecraft:lava' or block.name == 'minecraft:flowing_lava' then
if turtle.selectSlot('minecraft:bucket:0') then
if action.place() then
log('Lava! ' .. turtle.getFuelLevel())
turtle.refuel()
log(turtle.getFuelLevel())
end
turtle.select(1)
end
return false
end
end
if action.side == 'bottom' then
return block.name
end
if trash[block.name .. ':0'] then
return false
end
return block.name
end
function fortuneDig(action, blockName)
if options.fortunePick.value and fortuneBlocks[blockName] then
turtle.selectSlot('cctweaks:toolHost')
turtle.equipRight()
turtle.selectSlot(options.fortunePick.value)
repeat until not turtle.dig()
turtle.selectSlot('minecraft:diamond_pickaxe')
turtle.equipRight()
turtle.select(1)
return true
end
end
function mine(action)
local blockName = mineable(action)
if blockName then
checkSpace()
--collectDrops(action.suck)
if not fortuneDig(action, blockName) then
action.dig()
end
end
end
function bore()
local loc = turtle.point
local level = loc.y
turtle.select(1)
status('boring down')
boreDirection = 'down'
while true do
if turtle.abort then
status('aborting')
return false
end
if loc.y <= -mining.depth then
break
end
if turtle.point.y < -2 then
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
end
mine(turtle.getAction('down'))
if not Util.tryTimed(3, turtle.down) then
break
end
if loc.y < level - 1 then
mine(turtle.getAction('forward'))
turtle.turnRight()
mine(turtle.getAction('forward'))
end
end
boreDirection = 'up'
status('boring up')
turtle.turnRight()
mine(turtle.getAction('forward'))
turtle.turnRight()
mine(turtle.getAction('forward'))
turtle.turnLeft()
while true do
if turtle.abort then
status('aborting')
return false
end
if turtle.point.y > -2 then
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
end
while not Util.tryTimed(3, turtle.up) do
status('stuck')
end
if turtle.status == 'stuck' then
status('boring up')
end
if loc.y >= level - 1 then
break
end
mine(turtle.getAction('forward'))
turtle.turnLeft()
mine(turtle.getAction('forward'))
end
if turtle.getFuelLevel() < LOW_FUEL then
refuel()
local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0}) + 512
if turtle.getFuelLevel() < veryMinFuel then
log('Not enough fuel to continue')
return false
end
end
return true
end
function checkSpace()
if turtle.getItemCount(16) > 0 then
refuel()
local oldStatus = turtle.status
status('condensing')
ejectTrash()
turtle.condense()
local lastSlot = 16
if boreDirection == 'down' then
lastSlot = 15
end
if turtle.getItemCount(lastSlot) > 0 then
unload()
end
status(oldStatus)
turtle.select(1)
end
end
function collectDrops(suckAction)
for i = 1, 50 do
if not suckAction() then
break
end
checkSpace()
end
end
function Point.compare(pta, ptb)
if pta.x == ptb.x and pta.z == ptb.z then
if pta.y and ptb.y then
return pta.y == ptb.y
end
return true
end
return false
end
function inspect(action, name)
local r, block = action.inspect()
if r and block.name == name then
return true
end
end
function boreCommand()
local pt = getClosestLocation(mining.locations, turtle.point)
turtle.setMoveCallback(function(action, tpt)
makeWalkableTunnel(action, tpt, pt)
end)
safeGotoY(0)
safeGoto(pt.x, pt.z, 0)
turtle.clearMoveCallback()
-- location is either mined, currently being mined or is the
-- dropoff point for a turtle
if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or
inspect(turtle.getAction('up'), 'minecraft:chest') or
inspect(turtle.getAction('down'), 'minecraft:cobblestone') then
return true
end
turtle.digUp()
turtle.placeUp('minecraft:cobblestone:0')
local success = bore()
safeGotoY(0) -- may have aborted
turtle.digUp()
if success then
turtle.placeDown('minecraft:cobblestone:0') -- cap with cobblestone to indicate this spot was mined out
end
return success
end
if not Util.getOptions(options, args) then
return
end
mining.depth = options.depth.value
mining.chunks = options.chunks.value
unload = normalChestUnload
--if options.enderChest.value then
-- unload = enderChestUnload
--end
mining.x = 0
mining.z = 0
mining.locations = getBoreLocations(0, 0)
trash = Util.readTable('mining.trash')
if options.resume.value then
mining = Util.readTable('mining.progress')
elseif fs.exists('mining.progress') then
print('use -r to resume')
read()
end
if not trash or options.setTrash.value then
print('Add trash blocks, press enter when ready')
read()
addTrash()
end
if not turtle.getSlot('minecraft:bucket:0') or
not turtle.getSlot('minecraft:cobblestone:0') then
print('Add bucket and cobblestone, press enter when ready')
read()
end
if options.fortunePick.value then
local s = turtle.getSlot(options.fortunePick.value)
if not s then
error('fortunePick not found: ' .. options.fortunePick.value)
end
if not turtle.getSlot('cctweaks:toolHost:0') then
error('CCTweaks tool host not found')
end
trash[s.iddmg] = nil
trash['minecraft:diamond_pickaxe:0'] = nil
trash['cctweaks:toolHost:0'] = nil
end
_G._p = trash
local function main()
repeat
while #mining.locations > 0 do
status('searching')
if not boreCommand() then
return
end
Util.writeTable('mining.progress', mining)
end
until not nextChunk()
end
turtle.run(function()
turtle.reset()
turtle.setPolicy(turtle.policies.digAttack)
turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
unload()
status('mining')
local s, m = pcall(function() main() end)
if not s and m then
printError(m)
end
safeGotoY(0)
safeGoto(0, 0, 0, 0)
unload()
turtle.reset()
end)

View File

@@ -0,0 +1,170 @@
require = requireInjector(getfenv(1))
local Util = require('util')
local Event = require('event')
local UI = require('ui')
local RefinedProvider = require('refinedProvider')
local MEProvider = require('meProvider')
local storage = RefinedProvider()
if not storage:isValid() then
storage = MEProvider()
end
if not storage:isValid() then
error('Not connected to a storage device')
end
multishell.setTitle(multishell.getCurrent(), 'Storage Activity')
UI:configure('StorageActivity', ...)
local changedPage = UI.Page({
grid = UI.Grid({
columns = {
{ heading = 'Qty', key = 'qty', width = 5 },
{ heading = 'Change', key = 'change', width = 6 },
{ heading = 'Name', key = 'display_name', width = UI.term.width - 15 },
},
sortColumn = 'display_name',
rey = -6,
}),
buttons = UI.Window({
ry = -4,
height = 5,
backgroundColor = colors.gray,
prevButton = UI.Button({
event = 'previous',
backgroundColor = colors.lightGray,
x = 2,
y = 2,
height = 3,
width = 5,
text = ' < '
}),
resetButton = UI.Button({
event = 'reset',
backgroundColor = colors.lightGray,
x = 8,
y = 2,
height = 3,
rex = -8,
text = 'Reset'
}),
nextButton = UI.Button({
event = 'next',
backgroundColor = colors.lightGray,
rx = -5,
y = 2,
height = 3,
width = 5,
text = ' > '
})
}),
accelerators = {
q = 'quit',
}
})
function changedPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
local ind = '+'
if row.change < 0 then
ind = ''
end
row.change = ind .. Util.toBytes(row.change)
row.qty = Util.toBytes(row.qty)
return row
end
function changedPage:eventHandler(event)
if event.type == 'reset' then
self.lastItems = nil
self.grid:setValues({ })
self.grid:clear()
self.grid:draw()
elseif event.type == 'next' then
self.grid:nextPage()
elseif event.type == 'previous' then
self.grid:previousPage()
elseif event.type == 'quit' then
Event.exitPullEvents()
else
return UI.Page.eventHandler(self, event)
end
return true
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
function changedPage:refresh()
local t = storage:listItems('all')
if not t or Util.empty(t) then
self:clear()
self:centeredWrite(math.ceil(self.height/2), 'Communication failure')
return
end
for k,v in pairs(t) do
t[k] = Util.shallowCopy(v)
end
if not self.lastItems then
self.lastItems = t
self.grid:setValues({ })
else
local changedItems = {}
for _,v in pairs(self.lastItems) do
found = false
for k2,v2 in pairs(t) do
if uniqueKey(v) == uniqueKey(v2) then
if v.qty ~= v2.qty then
local c = Util.shallowCopy(v2)
c.lastQty = v.qty
table.insert(changedItems, c)
end
table.remove(t, k2)
found = true
break
end
end
-- New item
if not found then
local c = Util.shallowCopy(v)
c.lastQty = v.qty
c.qty = 0
table.insert(changedItems, c)
end
end
-- No items left
for k,v in pairs(t) do
v.lastQty = 0
table.insert(changedItems, v)
end
for k,v in pairs(changedItems) do
v.change = v.qty - v.lastQty
end
self.grid:setValues(changedItems)
end
self.grid:draw()
end
Event.addTimer(5, true, function()
changedPage:refresh()
changedPage:sync()
end)
UI:setPage(changedPage)
UI:pullEvents()
UI.term:reset()

905
sys/apps/storageManager.lua Normal file
View File

@@ -0,0 +1,905 @@
require = requireInjector(getfenv(1))
local Event = require('event')
local UI = require('ui')
local ME = require('me')
local Config = require('config')
local Logger = require('logger')
-- Must be a crafty turtle with duck antenna !
-- 3 wide monitor (any side of turtle)
-- Config location is /sys/config/storageMonitor
-- adjust directions in that file if needed
local config = {
trashDirection = 'up', -- trash /chest in relation to interface
turtleDirection = 'down', -- turtle in relation to interface
noCraftingStorage = 'false' -- no ME crafting (or ability to tell if powered - use with caution)
}
Config.load('storageMonitor', config)
if not device.tileinterface then
error('ME interface not found')
end
local duckAntenna
if device.workbench then
local oppositeSide = {
[ 'left' ] = 'right',
[ 'right' ] = 'left'
}
local duckAntennaSide = oppositeSide[device.workbench.side]
duckAntenna = peripheral.wrap(duckAntennaSide)
end
--if not device.monitor then
-- error('Monitor not found')
--end
ME.setDevice(device.tileinterface)
local jobListGrid
local craftingPaused = false
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
Logger.disable()
function getItem(items, inItem, ignore_dmg)
for _,item in pairs(items) do
if item.id == inItem.id then
if ignore_dmg and ignore_dmg == 'yes' then
return item
elseif item.dmg == inItem.dmg and item.nbt_hash == inItem.nbt_hash then
return item
end
end
end
end
local function uniqueKey(item)
local key = item.id .. ':' .. item.dmg
if item.nbt_hash then
key = key .. ':' .. item.nbt_hash
end
return key
end
function mergeResources(t)
local resources = Util.readTable('resource.limits')
resources = resources or { }
for _,item in pairs(t) do
item.has_recipe = false
end
for _,v in pairs(resources) do
local item = getItem(t, v)
if item then
item.limit = tonumber(v.limit)
item.low = tonumber(v.low)
item.auto = v.auto
item.ignore_dmg = v.ignore_dmg
else
v.qty = 0
v.limit = tonumber(v.limit)
v.low = tonumber(v.low)
v.auto = v.auto
v.ignore_dmg = v.ignore_dmg
table.insert(t, v)
end
end
recipes = Util.readTable('recipes') or { }
for _,v in pairs(recipes) do
local item = getItem(t, v)
if item then
item.has_recipe = true
else
v.qty = 0
v.limit = nil
v.low = nil
v.has_recipe = true
v.auto = 'no'
v.ignore_dmg = 'no'
v.has_recipe = 'true'
table.insert(t, v)
end
end
end
function filterItems(t, filter)
local r = {}
if filter then
filter = filter:lower()
for k,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
else
return t
end
return r
end
function sumItems(items)
local t = {}
for _,item in pairs(items) do
local key = uniqueKey(item)
local summedItem = t[key]
if summedItem then
summedItem.qty = summedItem.qty + item.qty
else
summedItem = Util.shallowCopy(item)
t[key] = summedItem
end
end
return t
end
function isGridClear()
for i = 1, 16 do
if turtle.getItemCount(i) ~= 0 then
return false
end
end
return true
end
local function clearGrid()
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
ME.insert(i, count, config.turtleDirection)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
function turtleCraft(recipe, originalItem)
for k,v in pairs(recipe.ingredients) do
-- ugh
local dmg = v.dmg
if v.max_dmg and v.max_dmg > 0 then
local item = ME.getItemDetail({ id = v.id, nbt_hash = v.nbt_hash }, false)
if item then
dmg = item.dmg
end
end
if not ME.extract(v.id, dmg, v.nbt_hash, v.qty, config.turtleDirection, k) then
clearGrid()
originalItem.status = v.name .. ' (extract failed)'
return false
end
end
if not turtle.craft() then
clearGrid()
return false
end
clearGrid()
return true
end
function craftItem(items, recipes, item, originalItem, itemList)
local key = uniqueKey(item)
local recipe = recipes[key]
if recipe then
if not isGridClear() then
return
end
local summedItems = sumItems(recipe.ingredients)
for i = 1, math.ceil(item.qty / recipe.qty) do
local failed = false -- try to craft all components (use all CPUs available)
for _,ingredient in pairs(summedItems) do
local ignore_dmg = 'no'
if ingredient.max_dmg and ingredient.max_dmg > 0 then
ignore_dmg = 'yes'
end
local qty = ME.getItemCount(ingredient.id, ingredient.dmg, ingredient.nbt_hash, ignore_dmg)
if qty < ingredient.qty then
originalItem.status = ingredient.name .. ' (crafting)'
ingredient.qty = ingredient.qty - qty
if not craftItem(items, recipes, ingredient, originalItem, itemList) then
failed = true
end
end
end
if failed then
return false
end
if not failed and not turtleCraft(recipe, originalItem) then
Logger.debug('turtle failed to craft ' .. item.name)
return false
end
end
return true
else
local meItem = getItem(items, item)
if not meItem or not meItem.is_craftable then
if item.id == originalItem.id and item.dmg == originalItem.dmg then
originalItem.status = '(not craftable)'
else
originalItem.status = item.name .. ' (missing)'
end
else
if item.id == originalItem.id and item.dmg == originalItem.dmg then
item.meCraft = true
return false
end
-- find it in the list of items to be crafted
for _,v in pairs(itemList) do
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
v.qty = item.qty + v.qty
return false
end
end
-- add to the item list
table.insert(itemList, {
id = item.id,
dmg = item.dmg,
nbt_hash = item.nbt_hash,
qty = item.qty,
name = item.name,
meCraft = true,
status = ''
})
end
end
return false
end
function craftItems(itemList)
local recipes = Util.readTable('recipes') or { }
local items = ME.getAvailableItems()
-- turtle craft anything we can, build up list for ME items
local keys = Util.keys(itemList)
for _,key in pairs(keys) do
local item = itemList[key]
craftItem(items, recipes, item, item, itemList)
end
-- second pass is to request crafting from ME with aggregated items
for _,item in pairs(itemList) do
if item.meCraft then
local alreadyCrafting = false
local jobList = ME.getJobList()
for _,v in pairs(jobList) do
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
alreadyCrafting = true
end
end
if alreadyCrafting then
item.status = '(crafting)'
elseif not ME.isCPUAvailable() then
item.status = '(waiting)'
else
item.status = '(failed)'
local qty = item.qty
while qty >= 1 do -- try to request smaller quantities until successful
if ME.craft(item.id, item.dmg, item.nbt_hash, qty) then
item.status = '(crafting)'
break -- successfully requested crafting
end
qty = math.floor(qty / 2)
end
end
end
end
end
-- AE 1 (obsolete)
function isCrafting(jobList, id, dmg)
for _, job in pairs(jobList) do
if job.id == id and job.dmg == dmg then
return job
end
end
end
local nullDevice = {
setCursorPos = function(...) end,
write = function(...) end,
getSize = function() return 13, 20 end,
isColor = function() return false end,
setBackgroundColor = function(...) end,
setTextColor = function(...) end,
clear = function(...) end,
}
local function jobMonitor(jobList)
local mon
if device.monitor then
mon = UI.Device({
deviceType = 'monitor',
textScale = .5,
})
else
mon = UI.Device({
device = nullDevice
})
end
jobListGrid = UI.Grid({
parent = mon,
sortColumn = 'name',
columns = {
{ heading = 'Qty', key = 'qty', width = 6 },
{ heading = 'Crafting', key = 'name', width = mon.width / 2 - 10 },
{ heading = 'Status', key = 'status', width = mon.width - 10 },
},
})
end
function getAutocraftItems(items)
local t = Util.readTable('resource.limits') or { }
local itemList = { }
for _,res in pairs(t) do
if res.auto and res.auto == 'yes' then
res.qty = 4 -- this could be higher to increase autocrafting speed
table.insert(itemList, res)
end
end
return itemList
end
local function getItemWithQty(items, res, ignore_dmg)
local item = getItem(items, res, ignore_dmg)
if item then
if ignore_dmg and ignore_dmg == 'yes' then
local qty = 0
for _,v in pairs(items) do
if item.id == v.id and item.nbt_hash == v.nbt_hash then
if item.max_dmg > 0 or item.dmg == v.dmg then
qty = qty + v.qty
end
end
end
item.qty = qty
end
end
return item
end
function watchResources(items)
local itemList = { }
local t = Util.readTable('resource.limits') or { }
for k, res in pairs(t) do
local item = getItemWithQty(items, res, res.ignore_dmg)
res.limit = tonumber(res.limit)
res.low = tonumber(res.low)
if not item then
item = {
id = res.id,
dmg = res.dmg,
nbt_hash = res.nbt_hash,
name = res.name,
qty = 0
}
end
if res.limit and item.qty > res.limit then
Logger.debug("Purging " .. item.qty-res.limit .. " " .. res.name)
if not ME.extract(item.id, item.dmg, item.nbt_hash, item.qty - res.limit, config.trashDirection) then
Logger.debug('Failed to purge ' .. res.name)
end
elseif res.low and item.qty < res.low then
if res.ignore_dmg and res.ignore_dmg == 'yes' then
item.dmg = 0
end
table.insert(itemList, {
id = item.id,
dmg = item.dmg,
nbt_hash = item.nbt_hash,
qty = res.low - item.qty,
name = item.name,
status = ''
})
end
end
return itemList
end
itemPage = UI.Page {
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green
},
idField = UI.Text {
x = 5, y = 3, width = UI.term.width - 10,
},
form = UI.Form {
x = 4, y = 4, height = 8, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Craft until out of ingredients'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Ignore damage of item'
},
},
statusBar = UI.StatusBar { }
}
function itemPage:enable()
UI.Page.enable(self)
self:focusFirst()
end
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
self.statusBar:draw()
elseif event.type == 'form_complete' then
local values = self.form.values
local t = Util.readTable('resource.limits') or { }
for k,v in pairs(t) do
if v.id == values.id and v.dmg == values.dmg then
table.remove(t, k)
break
end
end
local keys = { 'name', 'auto', 'id', 'low', 'dmg', 'max_dmg', 'nbt_hash', 'limit', 'ignore_dmg' }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
table.insert(t, filtered)
Util.writeTable('resource.limits', t)
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Learn', event = 'learn' },
{ text = 'Forget', event = 'forget' },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'name' , width = 22 },
{ heading = 'Qty', key = 'qty' , width = 5 },
{ heading = 'Min', key = 'low' , width = 4 },
{ heading = 'Max', key = 'limit', width = 4 },
},
sortColumn = 'name',
},
statusBar = UI.StatusBar {
backgroundColor = colors.gray,
width = UI.term.width,
filterText = UI.Text {
x = 2, width = 6,
value = 'Filter',
},
filter = UI.TextEntry {
x = 9, width = 19,
limit = 50,
},
refresh = UI.Button {
x = 31, width = 8,
text = 'Refresh',
event = 'refresh',
},
},
accelerators = {
r = 'refresh',
q = 'quit',
}
}
function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then
return colors.yellow
end
if row.has_recipe then
if selected then
return colors.blue
end
return colors.lightBlue
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.qty = Util.toBytes(row.qty)
if row.low then
row.low = Util.toBytes(row.low)
end
if row.limit then
row.limit = Util.toBytes(row.limit)
end
return row
end
function listingPage.statusBar:draw()
return UI.Window.draw(self)
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
itemPage.form:setValues(selected)
itemPage.titleBar.title = selected.name
itemPage.idField.value = selected.id
UI:setPage('item')
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
elseif event.type == 'learn' then
if not duckAntenna then
self.statusBar:timedStatus('Missing peripherals', 3)
else
UI:setPage('craft')
end
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local recipes = Util.readTable('recipes') or { }
local key = uniqueKey(item)
local recipe = recipes[key]
if recipe then
recipes[key] = nil
Util.writeTable('recipes', recipes)
end
local resources = Util.readTable('resource.limits') or { }
for k,v in pairs(resources) do
if v.id == item.id and v.dmg == item.dmg then
table.remove(resources, k)
Util.writeTable('resource.limits', resources)
break
end
end
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
self:refresh()
self.grid:draw()
end
elseif event.type == 'text_change' then
self.filter = event.text
if #self.filter == 0 then
self.filter = nil
end
self:applyFilter()
self.grid:draw()
self.statusBar.filter:focus()
else
UI.Page.eventHandler(self, event)
end
return true
end
function listingPage:enable()
self:refresh()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
end
function listingPage:refresh()
self.allItems = ME.getAvailableItems('all')
mergeResources(self.allItems)
Util.each(self.allItems, function(item)
item.lname = item.name:lower()
end)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
-- without duck antenna
local function getTurtleInventory()
local inventory = { }
for i = 1,16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
local item = turtle.getItemDetail()
inventory[i] = {
id = item.name,
dmg = item.damage,
qty = item.count,
name = item.name,
}
end
end
return inventory
end
-- Strip off color prefix
local function safeString(text)
local val = text:byte(1)
if val < 32 or val > 128 then
local newText = {}
for i = 4, #text do
local val = text:byte(i)
newText[i - 3] = (val > 31 and val < 127) and val or 63
end
return string.char(unpack(newText))
end
return text
end
local function filter(t, filter)
local keys = Util.keys(t)
for _,key in pairs(keys) do
if not Util.key(filter, key) then
t[key] = nil
end
end
end
local function learnRecipe(page)
local t = Util.readTable('recipes') or { }
local recipe = { }
local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory()
if ingredients then
turtle.select(1)
if turtle.craft() then
recipe = duckAntenna.getAllStacks(false) -- getTurtleInventory()
if recipe and recipe[1] then
recipe = recipe[1]
local key = uniqueKey(recipe)
clearGrid()
recipe.name = safeString(recipe.display_name)
filter(recipe, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size' })
for _,ingredient in pairs(ingredients) do
ingredient.name = safeString(ingredient.display_name)
filter(ingredient, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size', 'max_dmg' })
if ingredient.max_dmg > 0 then -- let's try this...
ingredient.dmg = 0
end
end
recipe.ingredients = ingredients
recipe.ignore_dmg = 'no'
t[key] = recipe
Util.writeTable('recipes', t)
listingPage.statusBar.filter:setValue(recipe.name)
listingPage.statusBar:timedStatus('Learned: ' .. recipe.name, 3)
listingPage.filter = recipe.name
listingPage:refresh()
listingPage.grid:draw()
return true
end
else
page.statusBar:timedStatus('Failed to craft', 3)
end
else
page.statusBar:timedStatus('No recipe defined', 3)
end
end
craftPage = UI.Dialog {
height = 7, width = UI.term.width - 6,
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Learn Recipe',
previousPage = true,
},
idField = UI.Text {
x = 5,
y = 3,
width = UI.term.width - 10,
value = 'Place recipe in turtle'
},
accept = UI.Button {
rx = -13, ry = -2,
text = 'Ok', event = 'accept',
},
cancel = UI.Button {
rx = -8, ry = -2,
text = 'Cancel', event = 'cancel'
},
statusBar = UI.StatusBar {
status = 'Crafting paused'
}
}
function craftPage:enable()
craftingPaused = true
self:focusFirst()
UI.Dialog.enable(self)
end
function craftPage:disable()
craftingPaused = false
UI.Dialog.disable(self)
end
function craftPage:eventHandler(event)
if event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'accept' then
if learnRecipe(self) then
UI:setPreviousPage()
end
else
return UI.Dialog.eventHandler(self, event)
end
return true
end
UI:setPages({
listing = listingPage,
item = itemPage,
craft = craftPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
clearGrid()
jobMonitor()
jobListGrid:draw()
jobListGrid:sync()
function craftingThread()
while true do
os.sleep(5)
if not craftingPaused then
local items = ME.getAvailableItems()
if Util.size(items) == 0 then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
jobListGrid:sync()
elseif config.noCraftingStorage ~= 'true' and #ME.getCraftingCPUs() <= 0 then -- only way to determine if AE is online
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
jobListGrid:sync()
else
local itemList = watchResources(items)
jobListGrid:setValues(itemList)
jobListGrid:draw()
jobListGrid:sync()
craftItems(itemList)
jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor
craftItems(itemList)
end
end
end
end
Event.pullEvents(craftingThread)
UI.term:reset()
jobListGrid.parent:reset()

403
sys/apps/supplier.lua Normal file
View File

@@ -0,0 +1,403 @@
require = requireInjector(getfenv(1))
local Logger = require('logger')
local Message = require('message')
local Event = require('event')
local Point = require('point')
local TableDB = require('tableDB')
local MEProvider = require('meProvider')
local ChestProvider = require('chestProvider')
if os.getVersion() == 1.8 then
ChestProvider = require('chestProvider18')
end
if not device.wireless_modem then
error('No wireless modem detected')
end
Logger.filter('modem_send', 'event', 'ui')
Logger.setWirelessLogging()
local __BUILDER_ID = 6
local Builder = {
version = '1.70',
ccVersion = nil,
slots = { },
index = 1,
fuelItem = { id = 'minecraft:coal', dmg = 0 },
resupplying = true,
ready = true,
}
--[[-- maxStackDB --]]--
local maxStackDB = TableDB({
fileName = 'maxstack.db',
tabledef = {
autokeys = false,
type = 'simple',
columns = {
{ label = 'Key', type = 'key', length = 8 },
{ label = 'Quantity', type = 'number', length = 2 }
}
}
})
function maxStackDB:get(id, dmg)
return self.data[id .. ':' .. dmg] or 64
end
function Builder:dumpInventory()
local success = true
for i = 1, 16 do
local qty = turtle.getItemCount(i)
if qty > 0 then
self.itemProvider:insert(i, qty)
end
if turtle.getItemCount(i) ~= 0 then
success = false
end
end
turtle.select(1)
return success
end
function Builder:dumpInventoryWithCheck()
while not self:dumpInventory() do
Builder:log('Unable to dump inventory')
print('Provider is full or missing - make space or replace')
print('Press enter to continue')
turtle.setHeading(0)
self.ready = false
read()
end
self.ready = true
end
function Builder:autocraft(supplies)
local t = { }
for i,s in pairs(supplies) do
local key = s.id .. ':' .. s.dmg
local item = t[key]
if not item then
item = {
id = s.id,
dmg = s.dmg,
qty = 0,
}
t[key] = item
end
item.qty = item.qty + (s.need-s.qty)
end
Builder.itemProvider:craftItems(t)
end
function Builder:refuel()
while turtle.getFuelLevel() < 4000 and self.fuelItem do
Builder:log('Refueling')
turtle.select(1)
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)
os.sleep(5)
else
turtle.refuel(64)
end
end
end
function Builder:log(...)
Logger.log('supplier', ...)
Util.print(...)
end
function Builder:getSupplies()
Builder.itemProvider:refresh()
local t = { }
for _,s in ipairs(self.slots) do
if s.need > 0 then
local item = Builder.itemProvider:getItemInfo(s.id, s.dmg)
if item then
if item.name then
s.name = item.name
end
local qty = math.min(s.need-s.qty, item.qty)
if qty + s.qty > item.max_size then
maxStackDB:add({ s.id, s.dmg }, item.max_size)
maxStackDB.dirty = true
maxStackDB:flush()
qty = item.max_size
s.need = qty
end
if qty > 0 then
self.itemProvider:provide(item, qty, s.index)
s.qty = turtle.getItemCount(s.index)
end
end
end
if s.qty < s.need then
table.insert(t, s)
local name = s.name or s.id .. ':' .. s.dmg
Builder:log('Need %d %s', s.need - s.qty, name)
end
end
return t
end
local function moveTowardsX(dx)
local direction = dx - turtle.point.x
local move
if direction == 0 then
return false
end
if direction > 0 and turtle.point.heading == 0 or
direction < 0 and turtle.point.heading == 2 then
move = turtle.forward
else
move = turtle.back
end
return move()
end
local function moveTowardsZ(dz)
local direction = dz - turtle.point.z
local move
if direction == 0 then
return false
end
if direction > 0 and turtle.point.heading == 1 or
direction < 0 and turtle.point.heading == 3 then
move = turtle.forward
else
move = turtle.back
end
return move()
end
function Builder:finish()
Builder.resupplying = true
Builder.ready = false
if turtle.gotoLocation('supplies') then
turtle.setHeading(0)
os.sleep(.1) -- random 'Computer is not connected' error...
Builder:dumpInventory()
Event.exitPullEvents()
print('Finished')
end
end
function Builder:gotoBuilder()
if Builder.lastPoint then
turtle.status = 'tracking'
while true do
local pt = Point.copy(Builder.lastPoint)
pt.y = pt.y + 3
if turtle.point.y ~= pt.y then
turtle.gotoY(pt.y)
else
local distance = Point.turtleDistance(turtle.point, pt)
if distance <= 3 then
Builder:log('Synchronized')
break
end
if turtle.point.heading % 2 == 0 then
if turtle.point.x == pt.x then
turtle.headTowardsZ(pt.z)
moveTowardsZ(pt.z)
else
moveTowardsX(pt.x)
end
elseif turtle.point.z ~= pt.z then
moveTowardsZ(pt.z)
else
turtle.headTowardsX(pt.x)
moveTowardsX(pt.x)
end
end
end
end
end
Message.addHandler('builder',
function(h, id, msg, distance)
if not id or id ~= __BUILDER_ID then
return
end
if not Builder.resupplying then
local pt = msg.contents
pt.y = pt.y + 3
turtle.status = 'supervising'
turtle.gotoYfirst(pt)
end
end)
Message.addHandler('supplyList',
function(h, id, msg, distance)
if not id or id ~= __BUILDER_ID then
return
end
turtle.status = 'resupplying'
Builder.resupplying = true
Builder.slots = msg.contents.slots
Builder.slotUid = msg.contents.uid
Builder:log('Received supply list ' .. Builder.slotUid)
os.sleep(0)
if not turtle.gotoLocation('supplies') then
Builder:log('Failed to go to supply location')
self.ready = false
Event.exitPullEvents()
end
os.sleep(.2) -- random 'Computer is not connected' error...
Builder:dumpInventoryWithCheck()
Builder:refuel()
while true do
local supplies = Builder:getSupplies()
if #supplies == 0 then
break
end
turtle.setHeading(0)
Builder:autocraft(supplies)
turtle.status = 'waiting'
os.sleep(5)
end
Builder:log('Got all supplies')
os.sleep(0)
Builder:gotoBuilder()
Builder.resupplying = false
end)
Message.addHandler('needSupplies',
function(h, id, msg, distance)
if not id or id ~= __BUILDER_ID then
return
end
if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then
Builder:log('No supplies ready')
Message.send(__BUILDER_ID, 'gotSupplies')
else
turtle.status = 'supplying'
Builder:log('Supplying')
os.sleep(0)
local pt = msg.contents.point
pt.y = turtle.getPoint().y
pt.heading = nil
if not turtle.gotoYfirst(pt) then -- location of builder
Builder.resupplying = true
Message.send(__BUILDER_ID, 'gotSupplies')
os.sleep(0)
if not turtle.gotoLocation('supplies') then
Builder:log('failed to go to supply location')
self.ready = false
Event.exitPullEvents()
end
return
end
pt.y = pt.y - 2 -- location where builder should go for the chest to be above
turtle.select(15)
turtle.placeDown()
os.sleep(.1) -- random computer not connected error
local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
for i = 1, 16 do
p:insert(i, 64)
end
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt })
Message.waitForMessage('thanks', 5, __BUILDER_ID)
--os.sleep(0)
--p.condenseItems()
for i = 1, 16 do
p:extract(i, 64)
end
turtle.digDown()
turtle.status = 'waiting'
end
end)
Message.addHandler('finished',
function(h, id)
if not id or id ~= __BUILDER_ID then
return
end
Builder:finish()
end)
Event.addHandler('turtle_abort',
function()
turtle.abort = false
turtle.status = 'aborting'
Builder:finish()
end)
local function onTheWay() -- parallel routine
while true do
local e, side, _id, id, msg, distance = os.pullEvent('modem_message')
if Builder.ready then
if id == __BUILDER_ID and msg and msg.type then
if msg.type == 'needSupplies' then
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true })
elseif msg.type == 'builder' then
Builder.lastPoint = msg.contents
end
end
end
end
end
local args = {...}
if #args < 1 then
error('Supply id for builder')
end
__BUILDER_ID = tonumber(args[1])
maxStackDB:load()
turtle.setPoint({ x = -1, z = -2, y = 0, heading = 0 })
turtle.saveLocation('supplies')
Builder.itemProvider = MEProvider()
if not Builder.itemProvider:isValid() then
Builder.itemProvider = ChestProvider()
if not Builder.itemProvider:isValid() then
error('A chest or ME interface must be below turtle')
end
end
turtle.run(function()
Event.pullEvents(onTheWay)
end)

98
sys/apps/t.lua Normal file
View File

@@ -0,0 +1,98 @@
function doCommand(command, moves)
--[[
if command == 'sl' then
local pt = GPS.getPoint()
if pt then
turtle.storeLocation(moves, pt)
end
return
end
--]]
local function format(value)
if type(value) == 'boolean' then
if value then return 'true' end
return 'false'
end
if type(value) ~= 'table' then
return value
end
local str
for k,v in pairs(value) do
if not str then
str = '{ '
else
str = str .. ', '
end
str = str .. k .. '=' .. tostring(v)
end
if str then
str = str .. ' }'
else
str = '{ }'
end
return str
end
local function runCommand(fn, arg)
local r = { fn(arg) }
if r[2] then
print(format(r[1]) .. ': ' .. format(r[2]))
elseif r[1] then
print(format(r[1]))
end
return r[1]
end
local cmds = {
[ 's' ] = turtle.select,
[ 'rf' ] = turtle.refuel,
[ 'gh' ] = function() turtle.pathfind({ x = 0, y = 0, z = 0, heading = 0}) end,
}
local repCmds = {
[ 'u' ] = turtle.up,
[ 'd' ] = turtle.down,
[ 'f' ] = turtle.forward,
[ 'r' ] = turtle.turnRight,
[ 'l' ] = turtle.turnLeft,
[ 'ta' ] = turtle.turnAround,
[ 'DD' ] = turtle.digDown,
[ 'DU' ] = turtle.digUp,
[ 'D' ] = turtle.dig,
[ 'p' ] = turtle.place,
[ 'pu' ] = turtle.placeUp,
[ 'pd' ] = turtle.placeDown,
[ 'b' ] = turtle.back,
[ 'gfl' ] = turtle.getFuelLevel,
[ 'gp' ] = turtle.getPoint,
[ 'R' ] = function() turtle.setPoint({x = 0, y = 0, z = 0, heading = 0}) return turtle.point end
}
if cmds[command] then
runCommand(cmds[command], moves)
elseif repCmds[command] then
for i = 1, moves do
if not runCommand(repCmds[command]) then
break
end
end
end
end
local args = {...}
if #args > 0 then
doCommand(args[1], args[2] or 1)
else
print('Enter command (q to quit):')
while true do
local cmd = read()
if cmd == 'q' then break
end
args = { }
cmd:gsub('%w+', function(w) table.insert(args, w) end)
doCommand(args[1], args[2] or 1)
end
end

81
sys/apps/telnet.lua Normal file
View File

@@ -0,0 +1,81 @@
require = requireInjector(getfenv(1))
local process = require('process')
local Socket = require('socket')
local Terminal = require('terminal')
local remoteId
local args = { ... }
if #args == 1 then
remoteId = tonumber(args[1])
else
print('Enter host ID')
remoteId = tonumber(read())
end
if not remoteId then
error('Syntax: telnet <host ID>')
end
print('connecting...')
local socket = Socket.connect(remoteId, 23)
if not socket then
error('Unable to connect to ' .. remoteId .. ' on port 23')
end
local ct = Util.shallowCopy(term.current())
if not ct.isColor() then
Terminal.toGrayscale(ct)
end
local w, h = ct.getSize()
socket:write({
type = 'termInfo',
width = w,
height = h,
isColor = ct.isColor(),
})
process:newThread('telnet_read', function()
while true do
local data = socket:read()
if not data then
break
end
for _,v in ipairs(data) do
ct[v.f](unpack(v.args))
end
end
end)
ct.clear()
ct.setCursorPos(1, 1)
local filter = Util.transpose({
'char', 'paste', 'key', 'key_up', 'mouse_scroll', 'mouse_click', 'mouse_drag',
})
while true do
local e = { process:pullEvent() }
local event = e[1]
if not socket.connected then
print()
print('Connection lost')
print('Press enter to exit')
read()
break
end
if filter[event] then
if not socket:write({ type = 'shellRemote', event = e }) then
socket:close()
break
end
elseif event == 'terminate' then
socket:close()
break
end
end

52
sys/apps/trace.lua Normal file
View File

@@ -0,0 +1,52 @@
local args = {...}
if not args[1] then
print("Usage:")
print(shell.getRunningProgram() .. " <program> [program arguments, ...]")
return
end
local path = shell.resolveProgram(args[1]) or shell.resolve(args[1])
-- here be dragons
if fs.exists(path) then
local eshell = setmetatable({getRunningProgram=function() return path end}, {__index = shell})
local env = setmetatable({shell=eshell}, {__index=_ENV})
local f = fs.open(path, "r")
local d = f.readAll()
f.close()
local func, e = load(d, fs.getName(path), nil, env)
if not func then
printError("Syntax error:")
printError(" " .. e)
else
table.remove(args, 1)
xpcall(function() func(unpack(args)) end, function(err)
local trace = {}
local i, hitEnd, _, e = 4, false
repeat
_, e = pcall(function() error("<tracemarker>", i) end)
i = i + 1
if e == "xpcall: <tracemarker>" then
hitEnd = true
break
end
table.insert(trace, e)
until i > 10
table.remove(trace)
if err:match("^" .. trace[1]:match("^(.-:%d+)")) then table.remove(trace, 1) end
printError("\nProgram has crashed! Stack trace:")
printError(err)
for i, v in ipairs(trace) do
printError(" at " .. v:match("^(.-:%d+)"))
end
if not hitEnd then
printError(" ...")
end
end)
end
else
printError("program not found")
end

48
sys/apps/trust.lua Normal file
View File

@@ -0,0 +1,48 @@
require = requireInjector(getfenv(1))
local Socket = require('socket')
local SHA1 = require('sha1')
local Terminal = require('terminal')
local Crypto = require('crypto')
local remoteId
local args = { ... }
if #args == 1 then
remoteId = tonumber(args[1])
else
print('Enter host ID')
remoteId = tonumber(read())
end
if not remoteId then
error('Syntax: trust <host ID>')
end
local password = Terminal.readPassword('Enter password: ')
if not password then
error('Invalid password')
end
print('connecting...')
local socket = Socket.connect(remoteId, 19)
if not socket then
error('Unable to connect to ' .. remoteId .. ' on port 19')
end
local publicKey = os.getPublicKey()
local password = SHA1.sha1(password)
socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, password))
local data = socket:read(2)
socket:close()
if data and data.success then
print(data.msg)
elseif data then
error(data.msg)
else
error('No response')
end

2
sys/apps/update.lua Normal file
View File

@@ -0,0 +1,2 @@
local options = table.concat({ ... }, ' ')
shell.run('pastebin run sj4VMVJj ' .. options)

88
sys/apps/vnc.lua Normal file
View File

@@ -0,0 +1,88 @@
require = requireInjector(getfenv(1))
local process = require('process')
local Socket = require('socket')
local Terminal = require('terminal')
local remoteId
local args = { ... }
if #args == 1 then
remoteId = tonumber(args[1])
else
print('Enter host ID')
remoteId = tonumber(read())
end
if not remoteId then
error('Syntax: vnc <host ID>')
end
multishell.setTitle(multishell.getCurrent(), 'VNC-' .. remoteId)
print('connecting...')
local socket = Socket.connect(remoteId, 5900)
if not socket then
error('Unable to connect to ' .. remoteId .. ' on port 5900')
end
local w, h = term.getSize()
socket:write({
type = 'termInfo',
width = w,
height = h,
isColor = term.isColor(),
})
local ct = Util.shallowCopy(term.current())
if not ct.isColor() then
Terminal.toGrayscale(ct)
end
process:newThread('vnc_read', function()
while true do
local data = socket:read()
if not data then
break
end
for _,v in ipairs(data) do
ct[v.f](unpack(v.args))
end
end
end)
ct.clear()
ct.setCursorPos(1, 1)
while true do
local e = { process:pullEvent() }
local event = e[1]
if not socket.connected then
print()
print('Connection lost')
print('Press enter to exit')
read()
break
end
if event == 'char' or
event == 'paste' or
event == 'key' or
event == 'key_up' or
event == 'mouse_scroll' or
event == 'mouse_click' or
event == 'mouse_drag' then
socket:write({
type = 'shellRemote',
event = e,
})
elseif event == 'terminate' then
socket:close()
ct.setBackgroundColor(colors.black)
ct.clear()
ct.setCursorPos(1, 1)
break
end
end

52
sys/autorun/gps.lua Normal file
View File

@@ -0,0 +1,52 @@
if turtle and device.wireless_modem then
local s, m = turtle.run(function()
local homePt = turtle.loadLocation('gpsHome')
if homePt then
require = requireInjector(getfenv(1))
local Config = require('config')
local config = {
destructive = false,
}
Config.load('gps', config)
local GPS = require('gps')
local pt
for i = 1, 3 do
pt = GPS.getPointAndHeading(2)
if pt then
break
end
end
if not pt and config.destructive then
turtle.setPolicy('turtleSafe')
pt = GPS.getPointAndHeading(2)
end
if not pt then
error('Unable to get GPS position')
end
if config.destructive then
turtle.setPolicy('turtleSafe')
end
Util.print('Setting turtle point to %d %d %d', pt.x, pt.y, pt.z)
turtle.setPoint(pt)
turtle.getState().coordSystem = 'GPS'
if not turtle.pathfind(homePt) then
error('Failed to return home')
end
end
end)
turtle.setPolicy('none')
if not s and m then
error(m)
end
end

View File

@@ -2,22 +2,20 @@ print('\nStarting multishell..')
LUA_PATH = '/sys/apis'
-- math.randomseed(os.clock()) -- totally broken
_G.Util = dofile('/sys/apis/util.lua')
_G.Util = dofile('sys/apis/util.lua')
_G.debug = function(...) Util.print(...) end
_G.requireInjector = dofile('/sys/apis/injector.lua')
_G.requireInjector = dofile('sys/apis/injector.lua')
os.run(Util.shallowCopy(getfenv(1)), '/sys/extensions/device.lua')
os.run(Util.shallowCopy(getfenv(1)), 'sys/extensions/device.lua')
-- vfs
local s, m = os.run(Util.shallowCopy(getfenv(1)), '/sys/extensions/vfs.lua')
local s, m = os.run(Util.shallowCopy(getfenv(1)), 'sys/extensions/vfs.lua')
if not s then
error(m)
end
-- process fstab
local mounts = Util.readFile('config/fstab')
local mounts = Util.readFile('usr/config/fstab')
if mounts then
for _,l in ipairs(Util.split(mounts)) do
if l:sub(1, 1) ~= '#' then
@@ -27,8 +25,16 @@ if mounts then
end
end
-- user environment
if not fs.exists('usr/apps') then
fs.makeDir('usr/apps')
end
if not fs.exists('usr/autorun') then
fs.makeDir('usr/autorun')
end
local env = Util.shallowCopy(getfenv(1))
env.multishell = { }
local _, m = os.run(env, '/apps/shell', '/apps/multishell')
local _, m = os.run(env, 'sys/apps/shell', 'sys/apps/multishell')
printError(m or 'Multishell aborted')

667
sys/etc/blockIds.csv Normal file
View File

@@ -0,0 +1,667 @@
Air,0,minecraft:air,Non-Solid Block
Stone,1,minecraft:stone,Solid Block
Granite,1:1,,Solid Block
Polished Granite,1:2,,Solid Block
Diorite,1:3,,Solid Block
Polished Diorite,1:4,,Solid Block
Andesite,1:5,,Solid Block
Polished Andesite,1:6,,Solid Block
Grass,2,minecraft:grass,Solid Block
Dirt,3,minecraft:dirt,Solid Block
Dirt (No Grass),3:1,,Solid Block
Podzol,3:2,,Solid Block
Cobblestone,4,minecraft:cobblestone,Solid Block
Wooden Plank (Oak),5,minecraft:planks,Solid Block
Wooden Plank (Spruce),5:1,,Solid Block
Wooden Plank (Birch),5:2,,Solid Block
Wooden Plank (Jungle),5:3,,Solid Block
Wooden Plank (Acacia),5:4,,Solid Block
Wooden Plank (Dark Oak),5:5,,Solid Block
Sapling (Oak),6,minecraft:sapling,Plant
Sapling (Spruce),6:1,,Plant
Sapling (Birch),6:2,,Plant
Sapling (Jungle),6:3,,Plant
Sapling (Acacia),6:4,,Plant
Sapling (Dark Oak),6:5,,Plant
Bedrock,7,minecraft:bedrock,Solid Block
Water,8,minecraft:flowing_water,Fluid
Water (No Spread),9,minecraft:water,Fluid
Lava,10,minecraft:flowing_lava,Fluid
Lava (No Spread),11,minecraft:lava,Fluid
Sand,12,minecraft:sand,Solid Block
Red Sand,12:1,,Solid Block
Gravel,13,minecraft:gravel,Solid Block
Gold Ore,14,minecraft:gold_ore,Solid Block
Iron Ore,15,minecraft:iron_ore,Solid Block
Coal Ore,16,minecraft:coal_ore,Solid Block
Wood (Oak),17,minecraft:log,Solid Block
Wood (Spruce),17:1,,Solid Block
Wood (Birch),17:2,,Solid Block
Wood (Jungle),17:3,,Solid Block
Wood (Oak 4),17:4,,Solid Block
Wood (Oak 5),17:5,,Solid Block
Leaves (Oak),18,minecraft:leaves,Solid Block
Leaves (Spruce),18:1,,Solid Block
Leaves (Birch),18:2,,Solid Block
Leaves (Jungle),18:3,,Solid Block
Sponge,19,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
Dispenser,23,minecraft:dispenser,Block
Sandstone,24,minecraft:sandstone,Solid Block
Sandstone (Chiseled),24:1,,Solid Block
Sandstone (Smooth),24:2,,Solid Block
Note Block,25,minecraft:noteblock,Solid Block
Bed (Block),26,minecraft:bed,Solid Block
Rail (Powered),27,minecraft:golden_rail,Non-Solid Block
Rail (Detector),28,minecraft:detector_rail,Non-Solid Block
Sticky Piston,29,minecraft:sticky_piston,Solid Block
Cobweb,30,minecraft:web,Non-Solid Block
Dead Shrub,31,minecraft:tallgrass,Plant
Tall Grass,31:1,,Plant
Fern,31:2,,Plant
Dead Shrub,32,minecraft:deadbush,Plant
Piston,33,minecraft:piston,Solid Block
Piston (Head),34,minecraft:piston_head,Solid Block
Wool,35,minecraft:wool,Solid Block
Orange Wool,35:1,,Solid Block
Magenta Wool,35:2,,Solid Block
Light Blue Wool,35:3,,Solid Block
Yellow Wool,35:4,,Solid Block
Lime Wool,35:5,,Solid Block
Pink Wool,35:6,,Solid Block
Gray Wool,35:7,,Solid Block
Light Gray Wool,35:8,,Solid Block
Cyan Wool,35:9,,Solid Block
Purple Wool,35:10,,Solid Block
Blue Wool,35:11,,Solid Block
Brown Wool,35:12,,Solid Block
Green Wool,35:13,,Solid Block
Red Wool,35:14,,Solid Block
Black Wool,35:15,,Solid Block
Piston (Moving),36,minecraft:piston_extension,Solid Block
Dandelion,37,minecraft:yellow_flower,Plant
Popppy,38,minecraft:red_flower,Plant
Blue Orchid,38:1,,Plant
Allium,38:2,,Plant
Azure Bluet,38:3,,
Red Tulip,38:4,,Plant
Orange Tulip,38:5,,Plant
White Tulip,38:6,,Plant
Pink Tulip,38:7,,Plant
Oxeye Daisy,38:8,,Plant
Brown Mushroom,39,minecraft:brown_mushroom,Block
Red Mushroom,40,minecraft:red_mushroom,Block
Block of Gold,41,minecraft:gold_block,Solid Block
Block of Iron,42,minecraft:iron_block,Solid Block
Stone Slab (Double),43,minecraft:double_stone_slab,Solid Block
Sandstone Slab (Double),43:1,,Solid Block
Wooden Slab (Double),43:2,,Solid Block
Cobblestone Slab (Double),43:3,,Solid Block
Brick Slab (Double),43:4,,Solid Block
Stone Brick Slab (Double),43:5,,Solid Block
Nether Brick Slab (Double),43:6,,Solid Block
Quartz Slab (Double),43:7,,Solid Block
Smooth Stone Slab (Double),43:8,,Solid Block
Smooth Sandstone Slab (Double),43:9,,Solid Block
Double Quartz Slab,43:15,,Solid Block
Stone Slab,44,minecraft:stone_slab,Solid Block
Sandstone Slab,44:1,,Solid Block
Wooden Slab,44:2,,Solid Block
Cobblestone Slab,44:3,,Solid Block
Brick Slab,44:4,,Solid Block
Stone Brick Slab,44:5,,Solid Block
Nether Brick Slab,44:6,,Solid Block
Quartz Slab,44:7,,Solid Block
Brick,45,minecraft:brick_block,Solid Block
TNT,46,minecraft:tnt,Solid Block
Bookshelf,47,minecraft:bookshelf,Solid Block
Moss Stone,48,minecraft:mossy_cobblestone,Solid Block
Obsidian,49,minecraft:obsidian,Solid Block
Torch,50,minecraft:torch,Non-Solid Block
Fire,51,minecraft:fire,Non-Solid Block
Mob Spawner,52,minecraft:mob_spawner,Solid Block
Wooden Stairs (Oak),53,minecraft:oak_stairs,Solid Block
Chest,54,minecraft:chest,Tile Entity
Redstone Wire,55,minecraft:redstone_wire,Raw Materials
Diamond Ore,56,minecraft:diamond_ore,Solid Block
Block of Diamond,57,minecraft:diamond_block,Solid Block
Crafting Table,58,minecraft:crafting_table,Solid Block
Wheat (Crop),59,minecraft:wheat,Plant
Farmland,60,minecraft:farmland,Solid Block
Furnace,61,minecraft:furnace,Solid Block
Furnace (Smelting),62,minecraft:lit_furnace,Solid Block
Sign (Block),63,minecraft:standing_sign,Non-Solid Block
Wood Door (Block),64,minecraft:wooden_door,Solid Block
Ladder,65,minecraft:ladder,Solid Block
Rail,66,minecraft:rail,Non-Solid Block
Cobblestair Stairs,67,minecraft:stone_stairs,Solid Block
Sign (Wall Block),68,minecraft:wall_sign,Non-Solid Block
Lever,69,minecraft:lever,Non-Solid Block
Stone Pressure Plate,70,minecraft:stone_pressure_plate,Non-Solid Block
Iron Door (Block),71,minecraft:iron_door,Solid Block
Wooden Pressure Plate,72,minecraft:wooden_pressure_plate,Non-Solid Blocks
Redstone Ore,73,minecraft:redstone_ore,Solid Block
Redstoen Ore (Glowing),74,minecraft:lit_redstone_ore,Solid Block
Redstone Torch (Off),75,minecraft:unlit_redstone_torch,Non-Solid Block
Redstone Torch,76,minecraft:redstone_torch,Non-Solid Block
Button (Stone),77,minecraft:stone_button,Non-Solid Block
Snow,78,minecraft:snow_layer,Block
Ice,79,minecraft:ice,Solid Block
Snow Block,80,minecraft:snow,Block
Cactus,81,minecraft:cactus,Solid Block
Clay Block,82,minecraft:clay,Solid Block
Sugar Can (Block),83,minecraft:reeds,Non-Solid Block
Jukebox,84,minecraft:jukebox,Solid Block
Fence,85,minecraft:fence,Solid Block
Pumpkin,86,minecraft:pumpkin,Solid Block
Netherrack,87,minecraft:netherrack,Solid Block
Soul Sand,88,minecraft:soul_sand,Solid Block
Glowstone,89,minecraft:glowstone,Solid Block
Portal,90,minecraft:portal,Non-Solid Block
Jack-O-Lantern,91,minecraft:lit_pumpkin,Solid Block
Cake (Block),92,minecraft:cake,Food
Redstone Repeater (Block Off),93,minecraft:unpowered_repeater,Solid Block
Redstone Repeater (Block On),94,minecraft:powered_repeater,Solid Block
Stained Glass (White),95,minecraft:stained_glass,Solid Block
Stained Glass (Orange),95:1,,Solid Block
Stained Glass (Magenta),95:2,,Solid Block
Stained Glass (Light Blue),95:3,,Solid Block
Stained Glass (Yellow),95:4,,Solid Block
Stained Glass (Lime),95:5,,Solid Block
Stained Glass (Pink),95:6,,Solid Block
Stained Glass (Gray),95:7,,Solid Block
Stained Glass (Light Gray),95:8,,Solid Block
Stained Glass (Cyan),95:9,,Solid Block
Stained Glass (Purple),95:10,,Solid Block
Stained Glass (Blue),95:11,,Solid Block
Stained Glass (Brown),95:12,,Solid Block
Stained Glass (Green),95:13,,Solid Block
Stained Glass (Red),95:14,,Solid Block
Stained Glass (Black),95:15,,Solid Block
Trapdoor,96,minecraft:trapdoor,Solid Block
Monster Egg (Stone),97,minecraft:monster_egg,Solid
Monster Egg (Cobblestone),97:1,,Solid
Monster Egg (Stone Brick),97:2,,Solid
Monster Egg (Mossy Stone Brick),97:3,,Solid
Monster Egg (Cracked Stone),97:4,,Solid
Monster Egg (Chiseled Stone),97:5,,Solid
Stone Brick,98,minecraft:stonebrick,Solid Block
Mossy Stone Brick,98:1,,Solid Block
Cracked Stone Brick,98:2,,Solid Block
Chiseled Stone Brick,98:3,,Solid Block
Brown Mushroom (Block),99,minecraft:brown_mushroom_block,Solid Block
Red Mushroom (Block),100,minecraft:red_mushroom_block,Solid Block
Iron Bars,101,minecraft:iron_bars,Solid Block
Glass Pane,102,minecraft:glass_pane,Solid Block
Melon (Block),103,minecraft:melon_block,Solid Block
Pumpkin Vine,104,minecraft:pumpkin_stem,
Melon Vine,105,minecraft:melon_stem,
Vines,106,minecraft:vine,
Fence Gate,107,minecraft:fence_gate,
Brick Stairs,108,minecraft:brick_stairs,
Stone Brick Stairs,109,minecraft:stone_brick_stairs,
Mycelium,110,minecraft:mycelium,
Lily Pad,111,minecraft:waterlily,
Nether Brick,112,minecraft:nether_brick,
Nether Brick Fence,113,minecraft:nether_brick_fence,
Nether Brick Stairs,114,minecraft:nether_brick_stairs,
Nether Wart,115,minecraft:nether_wart,
Enchantment Table,116,minecraft:enchanting_table,
Brewing Stand (Block),117,minecraft:brewing_stand,
Cauldron (Block),118,minecraft:cauldron,
End Portal,119,minecraft:end_portal,
End Portal Frame,120,minecraft:end_portal_frame,
End Stone,121,minecraft:end_stone,
Dragon Egg,122,minecraft:dragon_egg,
Redstone Lamp,123,minecraft:redstone_lamp,
Redstone Lamp (On),124,minecraft:lit_redstone_lamp,
Oak Wood Slab (Double),125,minecraft:double_wooden_slab,
Spruce Wood Slab (Double),125:1,,
Birch Wood Slab (Double),125:2,,
Jungle Wood Slab (Double),125:3,,
Acacia Wood Slab (Double),125:4,,
Dark Oak Wood Slab (Double),125:5,,
Oak Wood Slab,126,minecraft:wooden_slab,
Spruce Wood Slab,126:1,,
Birch Wood Slab,126:2,,
Jungle Wood Slab,126:3,,
Acacia Wood Slab,126:4,,
Dark Oak Wood Slab,126:5,,
Cocoa Plant,127,minecraft:cocoa,
Sandstone Stairs,128,minecraft:sandstone_stairs,
Emerald Ore,129,minecraft:emerald_ore,
Ender Chest,130,minecraft:ender_chest,
Tripwire Hook,131,minecraft:tripwire_hook,
Tripewire,132,minecraft:tripwire,
Block of Emerald,133,minecraft:emerald_block,
Wooden Stairs (Spruce),134,minecraft:spruce_stairs,
Wooden Stairs (Birch),135,minecraft:birch_stairs,
Wooden Stairs (Jungle),136,minecraft:jungle_stairs,
Command Block,137,minecraft:command_block,
Beacon,138,minecraft:beacon,
Cobblestone Wall,139,minecraft:cobblestone_wall,
Mossy Cobblestone Wall,139:1,,
Flower Pot (Block),140,minecraft:flower_pot,
Carrot (Crop),141,minecraft:carrots,
Potatoes (Crop),142,minecraft:potatoes,
Button (Wood),143,minecraft:wooden_button,
Head Block (Skeleton),144,minecraft:skull,
Head Block (Wither),144:1,,
Head Block (Zombie),144:2,,
Head Block (Steve),144:3,,
Head Block (Creeper),144:4,,
Anvil,145,minecraft:anvil,
Anvil (Slightly Damaged),145:1,,
Anvil (Very Damaged),145:2,,
Trapped Chest,146,minecraft:trapped_chest,
Weighted Pressure Plate (Light) (Gold),147,minecraft:light_weighted_pressure_plate,
Weighted Pressure Plate (Heavy) (Iron),148,minecraft:heavy_weighted_pressure_plate,
Redstone Comparator (Off),149,minecraft:unpowered_comparator,
Redstone Comparator (On),150,minecraft:powered_comparator,
Daylight Sensor,151,minecraft:daylight_detector,
Block of Redstone,152,minecraft:redstone_block,
Nether Quartz Ore,153,minecraft:quartz_ore,
Hopper,154,minecraft:hopper,
Quartz Block,155,minecraft:quartz_block,
Chiseled Quartz Block,155:1,,
Pillar Quartz Block,155:2,,
Quartz Stairs,156,minecraft:quartz_stairs,
Rail (Activator),157,minecraft:activator_rail,
Dropper,158,minecraft:dropper,
Stained Clay (White),159,minecraft:stained_hardened_clay,Solid Block
Stained Clay (Orange),159:1,,Solid Block
Stained Clay (Magenta),159:2,,Solid Block
Stained Clay (Light Blue),159:3,,Solid Block
Stained Clay (Yellow),159:4,,Solid Block
Stained Clay (Lime),159:5,,Solid Block
Stained Clay (Pink),159:6,,Solid Block
Stained Clay (Gray),159:7,,Solid Block
Stained Clay (Light Gray),159:8,,Solid Block
Stained Clay (Cyan),159:9,,Solid Block
Stained Clay (Purple),159:10,,Solid Block
Stained Clay (Blue),159:11,,Solid Block
Stained Clay (Brown),159:12,,Solid Block
Stained Clay (Green),159:13,,Solid Block
Stained Clay (Red),159:14,,Solid Block
Stained Clay (Black),159:15,,Solid Block
Stained Glass Pane (White),160,minecraft:stained_glass_pane,Solid Block
Stained Glass Pane (Orange),160:1,,Solid Block
Stained Glass Pane (Magenta),160:2,,Solid Block
Stained Glass Pane (Light Blue),160:3,,Solid Block
Stained Glass Pane (Yellow),160:4,,Solid Block
Stained Glass Pane (Lime),160:5,,Solid Block
Stained Glass Pane (Pink),160:6,,Solid Block
Stained Glass Pane (Gray),160:7,,Solid Block
Stained Glass Pane (Light Gray),160:8,,Solid Block
Stained Glass Pane (Cyan),160:9,,Solid Block
Stained Glass Pane (Purple),160:10,,Solid Block
Stained Glass Pane (Blue),160:11,,Solid Block
Stained Glass Pane (Brown),160:12,,Solid Block
Stained Glass Pane (Green),160:13,,Solid Block
Stained Glass Pane (Red),160:14,,Solid Block
Stained Glass Pane (Black),160:15,,Solid Block
Acacia Leaves,161,minecraft:leaves2,Solid Block
Dark Oak Leaves,161:1,,Solid Block
Acacia Wood,162,minecraft:log2,Solid Block
Dark Oak Wood,162:1,,Solid Block
Wooden Stairs (Acacia Oak),163,minecraft:acacia_stairs,
Wooden Stairs (Dark Oak),164,minecraft:dark_oak_stairs,
Slime Block,165,minecraft:slime,
Barrier,166,minecraft:barrier,
Iron Trapdoor,167,minecraft:iron_trapdoor,
Prismarine,168,minecraft:prismarine,
Prismarine Bricks,168:1,,
Dark Prismarine,168:2,,
Sea Lantern,169,minecraft:sea_lantern,
Hay Bale,170,minecraft:hay_block,
Carpet (White),171,minecraft:carpet,
Carpet (Orange),171:1,,
Carpet (Magenta),171:2,,
Carpet (Light Blue),171:3,,
Carpet (Yellow),171:4,,
Carpet (Lime),171:5,,
Carpet (Pink),171:6,,
Carpet (Gray),171:7,,
Carpet (Light Gray),171:8,,
Carpet (Cyan),171:9,,
Carpet (Purple),171:10,,
Carpet (Blue),171:11,,
Carpet (Brown),171:12,,
Carpet (Green),171:13,,
Carpet (Red),171:14,,
Carpet (Black),171:15,,
Hardened Clay,172,minecraft:hardened_clay,
Block of Coal,173,minecraft:coal_block,
Packed Ice,174,minecraft:packed_ice,
Sunflower,175,minecraft:double_plant,
Lilac,175:1,,
Double Tallgrass,175:2,,
Large Fern,175:3,,
Rose Bush,175:4,,
Peony,175:5,,
Free-standing Banner,176,minecraft:standing_banner,
Wall-mounted Banner,177,minecraft:wall_banner,
Inverted Daylight Sensor,178,minecraft:daylight_detector_inverted,
Red Sandstone,179,minecraft:red_sandstone,
Chiseled Red Sandstone,179:1,,
Smooth Red Sandstone,179:2,,
Red Sandstone Stairs,180,minecraft:red_sandstone_stairs,
Double Red Sandstone Slab,181,minecraft:double_stone_slab2,
Red Sandstone Slab,182,minecraft:stone_slab2,
Spruce Fence Gate,183,minecraft:spruce_fence_gate,
Birch Fence Gate,184,minecraft:birch_fence_gate,
Jungle Fence Gate,185,minecraft:jungle_fence_gate,
Dark Oak Fence Gate,186,minecraft:dark_oak_fence_gate,
Acacia Fence Gate,187,minecraft:acacia_fence_gate,
Spruce Fence,188,minecraft:spruce_fence,
Birch Fence,189,minecraft:birch_fence,
Jungle Fence,190,minecraft:jungle_fence,
Dark Oak Fence,191,minecraft:dark_oak_fence,
Acacia Fence,192,minecraft:acacia_fence,
Spruce Door Block,193,minecraft:spruce_door,
Birch Door Block,194,minecraft:birch_door,
Jungle Door Block,195,minecraft:jungle_door,
Acacia Door Block,196,minecraft:acacia_door,
Dark Oak Door Block,197,minecraft:dark_oak_door,
End Rod,198,minecraft:end_rod,
Chorus Plant,199,minecraft:chorus_plant,
Chorus Flower,200,minecraft:chorus_flower,
Purpur Block,201,minecraft:purpur_block,
Purpur Pillar,202,minecraft:purpur_pillar,
Purpur Stairs,203,minecraft:purpur_stairs,
Purpur Double Slab,204,minecraft:purpur_double_slab,
Purpur Slab,205,minecraft:purpur_slab,
End Stone Bricks,206,minecraft:end_bricks,
Beetroot Block,207,minecraft:beetroots,
Repeating Command Block,210,minecraft:repeating_command_block,
Chain Command Block,211,minecraft:chain_command_block,
Iron Shovel,256,minecraft:iron_shovel,
Iron Pickaxe,257,minecraft:iron_pickaxe,
Iron Axe,258,minecraft:iron_axe,
Flint and Steel,259,minecraft:flint_and_steel,
Apple,260,minecraft:apple,
Bow,261,minecraft:bow,
Arrow,262,minecraft:arrow,
Coal,263,minecraft:coal,
Charcoal,263:1,,
Diamond,264,minecraft:diamond,
Iron Ingot,265,minecraft:iron_ingot,
Gold Ingot,266,minecraft:gold_ingot,
Iron Sword,267,minecraft:iron_sword,
Wooden Sword,268,minecraft:wooden_sword,
Wooden Shovel,269,minecraft:wooden_shovel,
Wooden Pickaxe,270,minecraft:wooden_pickaxe,
Wooden Axe,271,minecraft:wooden_axe,
Stone Sword,272,minecraft:stone_sword,
Stone Shovel,273,minecraft:stone_shovel,
Stone Pickaxe,274,minecraft:stone_pickaxe,
Stone Axe,275,minecraft:stone_axe,
Diamond Sword,276,minecraft:diamond_sword,
Diamond Shovel,277,minecraft:diamond_shovel,
Diamond Pickaxe,278,minecraft:diamond_pickaxe,
Diamond Axe,279,minecraft:diamond_axe,
Stick,280,minecraft:stick,
Bowl,281,minecraft:bowl,
Mushroom Stew,282,minecraft:mushroom_stew,
Gold Sword,283,minecraft:golden_sword,
Gold Shovel,284,minecraft:golden_shovel,
Gold Pickaxe,285,minecraft:golden_pickaxe,
Gold Axe,286,minecraft:golden_axe,
String,287,minecraft:string,
Feather,288,minecraft:feather,
Gunpowder,289,minecraft:gunpowder,
Wooden Hoe,290,minecraft:wooden_hoe,
Stone Hoe,291,minecraft:stone_hoe,
Iron Hoe,292,minecraft:iron_hoe,
Diamond Hoe,293,minecraft:diamond_hoe,
Gold Hoe,294,minecraft:golden_hoe,
Wheat Seeds,295,minecraft:wheat_seeds,
Wheat,296,minecraft:wheat,
Bread,297,minecraft:bread,
Leather Helment,298,minecraft:leather_helmet,
Leather Tunic,299,minecraft:leather_chestplate,
Leather Leggings,300,minecraft:leather_leggings,
Leather Boots,301,minecraft:leather_boots,
Chainmail Helment,302,minecraft:chainmail_helmet,
Chainmail Chestplate,303,minecraft:chainmail_chestplate,
Chainmail Leggings,304,minecraft:chainmail_leggings,
Chainmail Boots,305,minecraft:chainmail_boots,
Iron Helment,306,minecraft:iron_helmet,
Iron Chestplate,307,minecraft:iron_chestplate,
Iron Leggings,308,minecraft:iron_leggings,
Iron Boots,309,minecraft:iron_boots,
Diamond Helment,310,minecraft:diamond_helmet,
Diamond Chestplate,311,minecraft:diamond_chestplate,
Diamond Leggings,312,minecraft:diamond_leggings,
Diamond Boots,313,minecraft:diamond_boots,
Gold Helment,314,minecraft:golden_helmet,
Gold Chestplate,315,minecraft:golden_chestplate,
Gold Leggings,316,minecraft:golden_leggings,
Gold Boots,317,minecraft:golden_boots,
Flint,318,minecraft:flint,
Raw Porkchop,319,minecraft:porkchop,
Cooked Porkchop,320,minecraft:cooked_porkchop,
Painting,321,minecraft:painting,
Golden Apple,322,minecraft:golden_apple,
Enchanted Golden Apple (Notch Apple),322:1,,
Sign,323,minecraft:sign,
Wooden Door,324,minecraft:wooden_door,
Bucket,325,minecraft:bucket,
Bucket (Water),326,minecraft:water_bucket,
Bucket (Lava),327,minecraft:lava_bucket,
Minecart,328,minecraft:minecart,
Saddle,329,minecraft:saddle,
Iron Door,330,minecraft:iron_door,
Redstone Dust,331,minecraft:redstone,
Snowball,332,minecraft:snowball,
Boat,333,minecraft:boat,
Leather,334,minecraft:leather,
Bucket (Milk),335,minecraft:milk_bucket,
Clay Brick,336,minecraft:brick,
Clay,337,minecraft:clay_ball,
Sugar Cane,338,minecraft:reeds,
Paper,339,minecraft:paper,
Book,340,minecraft:book,
Slime Ball,341,minecraft:slime_ball,
Minecart (Storage) (Chest),342,minecraft:chest_minecart,
Minecart (Powered) (Furance),343,minecraft:furnace_minecart,
Egg,344,minecraft:egg,
Compass,345,minecraft:compass,
Fishing Rod,346,minecraft:fishing_rod,
Clock,347,minecraft:clock,
Glowstone Dust,348,minecraft:glowstone_dust,
Raw Fish,349,minecraft:fish,
Raw Salmon,349:1,,
Clownfish,349:2,,
Pufferfish,349:3,,
Cooked Fish,350,minecraft:cooked_fished,
Cooked Salmon,350:1,,
Clownfish,350:2,,
Pufferfish,350:3,,
Ink Sack,351,minecraft:dye,
Rose Red Dye,351:1,,
Cactus Green Dye,351:2,,
Cocoa Bean,351:3,,
Lapis Lazuli,351:4,,
Purple Due,351:5,,
Cyan Dye,351:6,,
Light Gray Dye,351:7,,
Gray Dye,351:8,,
Pink Dye,351:9,,
Lime Dye,351:10,,
Dandelion Yellow Dye,351:11,,
Light Blue Dye,351:12,,
Magenta Dye,351:13,,
Orange Dye,351:14,,
Bone Meal,351:15,,
Bone,352,minecraft:bone,
Sugar,353,minecraft:sugar,
Cake,354,minecraft:cake,
Bed,355,minecraft:bed,
Redstone Repeater,356,minecraft:repeater,
Cookie,357,minecraft:cookie,
Map,358,minecraft:filled_map,
Shears,359,minecraft:shears,
Melon (Slice),360,minecraft:melon,
Pumplin Seeds,361,minecraft:pumpkin_seeds,
Melon Seeds,362,minecraft:melon_seeds,
Raw Beef,363,minecraft:beef,
Steak,364,minecraft:cooked_beef,
Raw Chicken,365,minecraft:chicken,
Cooked Chicken,366,minecraft:cooked_chicken,
Rotten Flesh,367,minecraft:rotten_flesh,
Ender Pearl,368,minecraft:ender_pearl,
Blaze Rod,369,minecraft:blaze_rod,
Ghast Tear,370,minecraft:ghast_tear,
Gold Nugget,371,minecraft:god_nugget,
Nether Wart,372,minecraft:nether_wart,
Water Bottle,373,minecraft:potion,Potion
Awkward Potion,373:16,,Potion
Thick Potion,373:32,,Potion
Mundane Potion,373:64,,Potion
Regeneration Potion (0:45),373:8193,,Potion
Swiftness Potion (3:00),373:8194,,Potion
Fire Resistance Potion (3:00),373:8195,,Potion
Poison Potion (0:45),373:8196,,Potion
Healing Potion,373:8197,,Potion
Night Vision Potion (3:00),373:8198,,Potion
Weakness Potion (1:30),373:8200,,Potion
Strength Potion (3:00),373:8201,,Potion
Slowness Potion (1:30),373:8202,,Potion
Harming Potion,373:8204,,Potion
Water Breathing Potion (3:00),373:8205,,Potion
Invisibility Potion (3:00),373:8206,,Potion
Regeneration Potion II (0:22),373:8225,,Potion
Swiftness Potion II (1:30),373:8226,,Potion
Poison Potion II (0:22),373:8228,,Potion
Healing Potion II,373:8229,,Potion
Strength Potion II (1:30),373:8233,,Potion
Harming Potion II,373:8236,,Potion
Regeneration Potion (2:00),373:8257,,Potion
Swiftness Potion (8:00),373:8258,,Potion
Fire Resistance Potion (8:00),373:8259,,Potion
Poison Potion (2:00),373:8260,,Potion
Night Vision Potion (8:00),373:8262,,Potion
Weakness Potion (4:00),373:8264,,Potion
Strength Potion (8:00),373:8265,,Potion
Slowness Potion (4:00),373:8266,,Potion
Water Breathing Potion (8:00),373:8269,,Potion
Invisbility Potion (8:00),373:8270,,Potion
Regeneration Potion II (1:00),373:8289,,Potion
Swiftness Potion II (4:00),373:8290,,Potion
Poison Potion II (1:00),373:8292,,Potion
Strength Potion II (4:00),373:8297,,Potion
Regeneration Splash (0:33),373:16385,,Potion
Swiftness Splash (2:15),373:16386,,Potion
Fire Resistance Splash (2:15),373:16387,,Potion
Poison Splash (0:33),373:16288,,Potion
Healing Splash,373:16389,,Potion
Night Vision Splash (2:15),373:16390,,Potion
Weakness Splash (1:07),373:16392,,Potion
Strength Splash (2:15),373:16393,,Potion
Slowness Splash (1:07),373:16394,,Potion
Harming Splash,373:16396,,Potion
Water Breathing Splash (2:15),373:16197,,Potion
Invisbility Splash (2:15),373:16338,,Potion
Regeneration Splash II (0:16),373:16417,,Potion
Swiftness Splash II (1:07),373:16418,,Potion
Poison Splash II (0:16),373:16420,,Potion
Healing Splash II,373:16421,,Potion
Strength Splash II (1:07),373:16425,,Potion
Harming Splash II,373:16428,,Potion
Regeneration Splash (1:30),373:16449,,Potion
Swiftness Splash (6:00),373:16450,,Potion
Fire Resistance Splash (6:00),373:16451,,Potion
Poison Splash (1:30),373:16452,,Potion
Night Vision Splash (6:00),373:16454,,Potion
Weakness Splash (3:00),373:16456,,Potion
Strength Splash (6:00),373:16457,,Potion
Slowness Splash (3:00),373:16458,,Potion
Water Breathing Splash (6:00),373:16461,,Potion
Invisbility Splash (6:00),373:16462,,Potion
Regeneration Splash II (0:45),373:16481,,Potion
Swiftness Splash II (3:00),373:16482,,Potion
Poison Splash II (0:45),373:16484,,Potion
Strength Splash II (3:00),373:16489,,Potion
Glass Bottle,374,minecraft:glass_bottle,
Spider Eye,375,minecraft:spider_eye,
Fermented Spider Eye,376,minecraft:fermented_spider_eye,
Blaze Powder,377,minecraft:blaze_powder,
Magma Cream,378,minecraft:magma_cream,
Brewing Stand,379,minecraft:brewing_stand,
Cauldron,380,minecraft:cauldron,
Eye of Ender,381,minecraft:ender_eye,
Glistering Melon,382,minecraft:speckled_melon,
Spawn Egg (Creeper),383:50,minecraft:spawn_egg,Item
Spawn Egg (Skeleton),383:51,,Item
Spawn Egg (Spider),383:52,,Item
Spawn Egg (Zombie),383:54,,Item
Spawn Egg (Slime),383:55,,Item
Spawn Egg (Ghast),383:56,,Item
Spawn Egg (Zombie Pigmen),383:57,,Item
Spawn Egg (Endermen),383:58,,Item
Spawn Egg (Cave Spider),383:59,,Item
Spawn Egg (Silverfish),383:60,,Item
Spawn Egg (Blaze),383:61,,Item
Spawn Egg (Magma Cube),383:62,,Item
Spawn Egg (Bat),383:65,,Item
Spawn Egg (Witch),383:66,,Item
Spawn Egg (Pig),383:90,,Item
Spawn Egg (Sheep),383:91,,Item
Spawn Egg (Cow),383:92,,Item
Spawn Egg (Chicken),383:93,,Item
Spawn Egg (Squid),383:94,,Item
Spawn Egg (Wolf),383:95,,Item
Spawn Egg (Mooshroom),383:96,,Item
Spawn Egg (Ocelot),383:98,,Item
Spawn Egg (Horse),383:100,,Item
Spawn Egg (Villager),383:120,,Item
Bottle o' Enchanting,384,minecraft:experience_bottle,
Fire Charge,385,minecraft:fire_charge,
Book and Quill,386,minecraft:writable_book,
Written Book,387,minecraft:written_book,
Emerald,388,minecraft:emerald,
Item Frame,389,minecraft:item_frame,
Flower Pot,390,minecraft:flower_pot,
Carrot,391,minecraft:carrot,
Potato,392,minecraft:potato,
Baked Potato,393,minecraft:baked_potato,
Poisonous Potato,394,minecraft:poisonous_potato,
Empty Map,395,minecraft:map,
Golden Carrot,396,minecraft:golden_carrot,
Head (Skeleton),397,minecraft:skull,
Head (Wither),397:1,,
Head (Zombie),397:2,,
Head (Steve),397:3,,
Head (Creeper),397:4,,
Carrot on a Stick,398,minecraft:carrot_on_a_stick,
Nether Star,399,minecraft:nether_star,
Pumpkin Pie,400,minecraft:pumpkin_pie,
Firework Rocket,401,minecraft:fireworks,
Firework Star,402,minecraft:firework_charge,
Enchanted Book,403,minecraft:enchanted_book,
Redstone Comparator,404,minecraft:comparator,
Nether Brick (Item),405,minecraft:netherbrick,
Nether Quartz,406,minecraft:quartz,
Minecart (TNT),407,minecraft:tnt_minecart,
Minecart (Hopper),408,minecraft:hopper_minecart,
Iron Horse Armor,417,minecraft:iron_horse_armor,
Gold Horse Armor,418,minecraft:golden_horse_armor,
Diamond Horse Armor,419,minecraft:diamond_horse_armor,
Lead,420,minecraft:lead,
Name Tag,421,minecraft:name_tag,
Minecart (Command Block),422,minecraft:command_block_minecart,
Music Disk (13),2256,minecraft:record_13,Decorations
Music Disk (Cat),2257,minecraft:record_cat,Decorations
Music Disk (Blocks),2258,minecraft:record_blocks,Decorations
Music Disk (Chrip),2259,minecraft:record_chirp,Decorations
Music Disk (Far),2260,minecraft:record_far,Decorations
Music Disk (Mall),2261,minecraft:record_mall,Decorations
Music Disk (Mellohi),2262,minecraft:record_mellohi,Decorations
Music Disk (Stal),2263,minecraft:record_stal,Decorations
Music Disk (Strad),2264,minecraft:record_strad,Decorations
Music Disk (Ward),2265,minecraft:record_ward,Decorations
Music Disk (11) (Broken),2266,minecraft:record_11,Decorations
Music Disk (Wait),2267,minecraft:record_wait,Decorations
1 Air 0 minecraft:air Non-Solid Block
2 Stone 1 minecraft:stone Solid Block
3 Granite 1:1 Solid Block
4 Polished Granite 1:2 Solid Block
5 Diorite 1:3 Solid Block
6 Polished Diorite 1:4 Solid Block
7 Andesite 1:5 Solid Block
8 Polished Andesite 1:6 Solid Block
9 Grass 2 minecraft:grass Solid Block
10 Dirt 3 minecraft:dirt Solid Block
11 Dirt (No Grass) 3:1 Solid Block
12 Podzol 3:2 Solid Block
13 Cobblestone 4 minecraft:cobblestone Solid Block
14 Wooden Plank (Oak) 5 minecraft:planks Solid Block
15 Wooden Plank (Spruce) 5:1 Solid Block
16 Wooden Plank (Birch) 5:2 Solid Block
17 Wooden Plank (Jungle) 5:3 Solid Block
18 Wooden Plank (Acacia) 5:4 Solid Block
19 Wooden Plank (Dark Oak) 5:5 Solid Block
20 Sapling (Oak) 6 minecraft:sapling Plant
21 Sapling (Spruce) 6:1 Plant
22 Sapling (Birch) 6:2 Plant
23 Sapling (Jungle) 6:3 Plant
24 Sapling (Acacia) 6:4 Plant
25 Sapling (Dark Oak) 6:5 Plant
26 Bedrock 7 minecraft:bedrock Solid Block
27 Water 8 minecraft:flowing_water Fluid
28 Water (No Spread) 9 minecraft:water Fluid
29 Lava 10 minecraft:flowing_lava Fluid
30 Lava (No Spread) 11 minecraft:lava Fluid
31 Sand 12 minecraft:sand Solid Block
32 Red Sand 12:1 Solid Block
33 Gravel 13 minecraft:gravel Solid Block
34 Gold Ore 14 minecraft:gold_ore Solid Block
35 Iron Ore 15 minecraft:iron_ore Solid Block
36 Coal Ore 16 minecraft:coal_ore Solid Block
37 Wood (Oak) 17 minecraft:log Solid Block
38 Wood (Spruce) 17:1 Solid Block
39 Wood (Birch) 17:2 Solid Block
40 Wood (Jungle) 17:3 Solid Block
41 Wood (Oak 4) 17:4 Solid Block
42 Wood (Oak 5) 17:5 Solid Block
43 Leaves (Oak) 18 minecraft:leaves Solid Block
44 Leaves (Spruce) 18:1 Solid Block
45 Leaves (Birch) 18:2 Solid Block
46 Leaves (Jungle) 18:3 Solid Block
47 Sponge 19 minecraft:sponge Solid Block
48 Glass 20 minecraft:glass Solid Block
49 Lapis Lazuli Ore 21 minecraft:lapis_ore Solid Block
50 Lapis Lazuli Block 22 minecraft:lapis_block Solid Block
51 Dispenser 23 minecraft:dispenser Block
52 Sandstone 24 minecraft:sandstone Solid Block
53 Sandstone (Chiseled) 24:1 Solid Block
54 Sandstone (Smooth) 24:2 Solid Block
55 Note Block 25 minecraft:noteblock Solid Block
56 Bed (Block) 26 minecraft:bed Solid Block
57 Rail (Powered) 27 minecraft:golden_rail Non-Solid Block
58 Rail (Detector) 28 minecraft:detector_rail Non-Solid Block
59 Sticky Piston 29 minecraft:sticky_piston Solid Block
60 Cobweb 30 minecraft:web Non-Solid Block
61 Dead Shrub 31 minecraft:tallgrass Plant
62 Tall Grass 31:1 Plant
63 Fern 31:2 Plant
64 Dead Shrub 32 minecraft:deadbush Plant
65 Piston 33 minecraft:piston Solid Block
66 Piston (Head) 34 minecraft:piston_head Solid Block
67 Wool 35 minecraft:wool Solid Block
68 Orange Wool 35:1 Solid Block
69 Magenta Wool 35:2 Solid Block
70 Light Blue Wool 35:3 Solid Block
71 Yellow Wool 35:4 Solid Block
72 Lime Wool 35:5 Solid Block
73 Pink Wool 35:6 Solid Block
74 Gray Wool 35:7 Solid Block
75 Light Gray Wool 35:8 Solid Block
76 Cyan Wool 35:9 Solid Block
77 Purple Wool 35:10 Solid Block
78 Blue Wool 35:11 Solid Block
79 Brown Wool 35:12 Solid Block
80 Green Wool 35:13 Solid Block
81 Red Wool 35:14 Solid Block
82 Black Wool 35:15 Solid Block
83 Piston (Moving) 36 minecraft:piston_extension Solid Block
84 Dandelion 37 minecraft:yellow_flower Plant
85 Popppy 38 minecraft:red_flower Plant
86 Blue Orchid 38:1 Plant
87 Allium 38:2 Plant
88 Azure Bluet 38:3
89 Red Tulip 38:4 Plant
90 Orange Tulip 38:5 Plant
91 White Tulip 38:6 Plant
92 Pink Tulip 38:7 Plant
93 Oxeye Daisy 38:8 Plant
94 Brown Mushroom 39 minecraft:brown_mushroom Block
95 Red Mushroom 40 minecraft:red_mushroom Block
96 Block of Gold 41 minecraft:gold_block Solid Block
97 Block of Iron 42 minecraft:iron_block Solid Block
98 Stone Slab (Double) 43 minecraft:double_stone_slab Solid Block
99 Sandstone Slab (Double) 43:1 Solid Block
100 Wooden Slab (Double) 43:2 Solid Block
101 Cobblestone Slab (Double) 43:3 Solid Block
102 Brick Slab (Double) 43:4 Solid Block
103 Stone Brick Slab (Double) 43:5 Solid Block
104 Nether Brick Slab (Double) 43:6 Solid Block
105 Quartz Slab (Double) 43:7 Solid Block
106 Smooth Stone Slab (Double) 43:8 Solid Block
107 Smooth Sandstone Slab (Double) 43:9 Solid Block
108 Double Quartz Slab 43:15 Solid Block
109 Stone Slab 44 minecraft:stone_slab Solid Block
110 Sandstone Slab 44:1 Solid Block
111 Wooden Slab 44:2 Solid Block
112 Cobblestone Slab 44:3 Solid Block
113 Brick Slab 44:4 Solid Block
114 Stone Brick Slab 44:5 Solid Block
115 Nether Brick Slab 44:6 Solid Block
116 Quartz Slab 44:7 Solid Block
117 Brick 45 minecraft:brick_block Solid Block
118 TNT 46 minecraft:tnt Solid Block
119 Bookshelf 47 minecraft:bookshelf Solid Block
120 Moss Stone 48 minecraft:mossy_cobblestone Solid Block
121 Obsidian 49 minecraft:obsidian Solid Block
122 Torch 50 minecraft:torch Non-Solid Block
123 Fire 51 minecraft:fire Non-Solid Block
124 Mob Spawner 52 minecraft:mob_spawner Solid Block
125 Wooden Stairs (Oak) 53 minecraft:oak_stairs Solid Block
126 Chest 54 minecraft:chest Tile Entity
127 Redstone Wire 55 minecraft:redstone_wire Raw Materials
128 Diamond Ore 56 minecraft:diamond_ore Solid Block
129 Block of Diamond 57 minecraft:diamond_block Solid Block
130 Crafting Table 58 minecraft:crafting_table Solid Block
131 Wheat (Crop) 59 minecraft:wheat Plant
132 Farmland 60 minecraft:farmland Solid Block
133 Furnace 61 minecraft:furnace Solid Block
134 Furnace (Smelting) 62 minecraft:lit_furnace Solid Block
135 Sign (Block) 63 minecraft:standing_sign Non-Solid Block
136 Wood Door (Block) 64 minecraft:wooden_door Solid Block
137 Ladder 65 minecraft:ladder Solid Block
138 Rail 66 minecraft:rail Non-Solid Block
139 Cobblestair Stairs 67 minecraft:stone_stairs Solid Block
140 Sign (Wall Block) 68 minecraft:wall_sign Non-Solid Block
141 Lever 69 minecraft:lever Non-Solid Block
142 Stone Pressure Plate 70 minecraft:stone_pressure_plate Non-Solid Block
143 Iron Door (Block) 71 minecraft:iron_door Solid Block
144 Wooden Pressure Plate 72 minecraft:wooden_pressure_plate Non-Solid Blocks
145 Redstone Ore 73 minecraft:redstone_ore Solid Block
146 Redstoen Ore (Glowing) 74 minecraft:lit_redstone_ore Solid Block
147 Redstone Torch (Off) 75 minecraft:unlit_redstone_torch Non-Solid Block
148 Redstone Torch 76 minecraft:redstone_torch Non-Solid Block
149 Button (Stone) 77 minecraft:stone_button Non-Solid Block
150 Snow 78 minecraft:snow_layer Block
151 Ice 79 minecraft:ice Solid Block
152 Snow Block 80 minecraft:snow Block
153 Cactus 81 minecraft:cactus Solid Block
154 Clay Block 82 minecraft:clay Solid Block
155 Sugar Can (Block) 83 minecraft:reeds Non-Solid Block
156 Jukebox 84 minecraft:jukebox Solid Block
157 Fence 85 minecraft:fence Solid Block
158 Pumpkin 86 minecraft:pumpkin Solid Block
159 Netherrack 87 minecraft:netherrack Solid Block
160 Soul Sand 88 minecraft:soul_sand Solid Block
161 Glowstone 89 minecraft:glowstone Solid Block
162 Portal 90 minecraft:portal Non-Solid Block
163 Jack-O-Lantern 91 minecraft:lit_pumpkin Solid Block
164 Cake (Block) 92 minecraft:cake Food
165 Redstone Repeater (Block Off) 93 minecraft:unpowered_repeater Solid Block
166 Redstone Repeater (Block On) 94 minecraft:powered_repeater Solid Block
167 Stained Glass (White) 95 minecraft:stained_glass Solid Block
168 Stained Glass (Orange) 95:1 Solid Block
169 Stained Glass (Magenta) 95:2 Solid Block
170 Stained Glass (Light Blue) 95:3 Solid Block
171 Stained Glass (Yellow) 95:4 Solid Block
172 Stained Glass (Lime) 95:5 Solid Block
173 Stained Glass (Pink) 95:6 Solid Block
174 Stained Glass (Gray) 95:7 Solid Block
175 Stained Glass (Light Gray) 95:8 Solid Block
176 Stained Glass (Cyan) 95:9 Solid Block
177 Stained Glass (Purple) 95:10 Solid Block
178 Stained Glass (Blue) 95:11 Solid Block
179 Stained Glass (Brown) 95:12 Solid Block
180 Stained Glass (Green) 95:13 Solid Block
181 Stained Glass (Red) 95:14 Solid Block
182 Stained Glass (Black) 95:15 Solid Block
183 Trapdoor 96 minecraft:trapdoor Solid Block
184 Monster Egg (Stone) 97 minecraft:monster_egg Solid
185 Monster Egg (Cobblestone) 97:1 Solid
186 Monster Egg (Stone Brick) 97:2 Solid
187 Monster Egg (Mossy Stone Brick) 97:3 Solid
188 Monster Egg (Cracked Stone) 97:4 Solid
189 Monster Egg (Chiseled Stone) 97:5 Solid
190 Stone Brick 98 minecraft:stonebrick Solid Block
191 Mossy Stone Brick 98:1 Solid Block
192 Cracked Stone Brick 98:2 Solid Block
193 Chiseled Stone Brick 98:3 Solid Block
194 Brown Mushroom (Block) 99 minecraft:brown_mushroom_block Solid Block
195 Red Mushroom (Block) 100 minecraft:red_mushroom_block Solid Block
196 Iron Bars 101 minecraft:iron_bars Solid Block
197 Glass Pane 102 minecraft:glass_pane Solid Block
198 Melon (Block) 103 minecraft:melon_block Solid Block
199 Pumpkin Vine 104 minecraft:pumpkin_stem
200 Melon Vine 105 minecraft:melon_stem
201 Vines 106 minecraft:vine
202 Fence Gate 107 minecraft:fence_gate
203 Brick Stairs 108 minecraft:brick_stairs
204 Stone Brick Stairs 109 minecraft:stone_brick_stairs
205 Mycelium 110 minecraft:mycelium
206 Lily Pad 111 minecraft:waterlily
207 Nether Brick 112 minecraft:nether_brick
208 Nether Brick Fence 113 minecraft:nether_brick_fence
209 Nether Brick Stairs 114 minecraft:nether_brick_stairs
210 Nether Wart 115 minecraft:nether_wart
211 Enchantment Table 116 minecraft:enchanting_table
212 Brewing Stand (Block) 117 minecraft:brewing_stand
213 Cauldron (Block) 118 minecraft:cauldron
214 End Portal 119 minecraft:end_portal
215 End Portal Frame 120 minecraft:end_portal_frame
216 End Stone 121 minecraft:end_stone
217 Dragon Egg 122 minecraft:dragon_egg
218 Redstone Lamp 123 minecraft:redstone_lamp
219 Redstone Lamp (On) 124 minecraft:lit_redstone_lamp
220 Oak Wood Slab (Double) 125 minecraft:double_wooden_slab
221 Spruce Wood Slab (Double) 125:1
222 Birch Wood Slab (Double) 125:2
223 Jungle Wood Slab (Double) 125:3
224 Acacia Wood Slab (Double) 125:4
225 Dark Oak Wood Slab (Double) 125:5
226 Oak Wood Slab 126 minecraft:wooden_slab
227 Spruce Wood Slab 126:1
228 Birch Wood Slab 126:2
229 Jungle Wood Slab 126:3
230 Acacia Wood Slab 126:4
231 Dark Oak Wood Slab 126:5
232 Cocoa Plant 127 minecraft:cocoa
233 Sandstone Stairs 128 minecraft:sandstone_stairs
234 Emerald Ore 129 minecraft:emerald_ore
235 Ender Chest 130 minecraft:ender_chest
236 Tripwire Hook 131 minecraft:tripwire_hook
237 Tripewire 132 minecraft:tripwire
238 Block of Emerald 133 minecraft:emerald_block
239 Wooden Stairs (Spruce) 134 minecraft:spruce_stairs
240 Wooden Stairs (Birch) 135 minecraft:birch_stairs
241 Wooden Stairs (Jungle) 136 minecraft:jungle_stairs
242 Command Block 137 minecraft:command_block
243 Beacon 138 minecraft:beacon
244 Cobblestone Wall 139 minecraft:cobblestone_wall
245 Mossy Cobblestone Wall 139:1
246 Flower Pot (Block) 140 minecraft:flower_pot
247 Carrot (Crop) 141 minecraft:carrots
248 Potatoes (Crop) 142 minecraft:potatoes
249 Button (Wood) 143 minecraft:wooden_button
250 Head Block (Skeleton) 144 minecraft:skull
251 Head Block (Wither) 144:1
252 Head Block (Zombie) 144:2
253 Head Block (Steve) 144:3
254 Head Block (Creeper) 144:4
255 Anvil 145 minecraft:anvil
256 Anvil (Slightly Damaged) 145:1
257 Anvil (Very Damaged) 145:2
258 Trapped Chest 146 minecraft:trapped_chest
259 Weighted Pressure Plate (Light) (Gold) 147 minecraft:light_weighted_pressure_plate
260 Weighted Pressure Plate (Heavy) (Iron) 148 minecraft:heavy_weighted_pressure_plate
261 Redstone Comparator (Off) 149 minecraft:unpowered_comparator
262 Redstone Comparator (On) 150 minecraft:powered_comparator
263 Daylight Sensor 151 minecraft:daylight_detector
264 Block of Redstone 152 minecraft:redstone_block
265 Nether Quartz Ore 153 minecraft:quartz_ore
266 Hopper 154 minecraft:hopper
267 Quartz Block 155 minecraft:quartz_block
268 Chiseled Quartz Block 155:1
269 Pillar Quartz Block 155:2
270 Quartz Stairs 156 minecraft:quartz_stairs
271 Rail (Activator) 157 minecraft:activator_rail
272 Dropper 158 minecraft:dropper
273 Stained Clay (White) 159 minecraft:stained_hardened_clay Solid Block
274 Stained Clay (Orange) 159:1 Solid Block
275 Stained Clay (Magenta) 159:2 Solid Block
276 Stained Clay (Light Blue) 159:3 Solid Block
277 Stained Clay (Yellow) 159:4 Solid Block
278 Stained Clay (Lime) 159:5 Solid Block
279 Stained Clay (Pink) 159:6 Solid Block
280 Stained Clay (Gray) 159:7 Solid Block
281 Stained Clay (Light Gray) 159:8 Solid Block
282 Stained Clay (Cyan) 159:9 Solid Block
283 Stained Clay (Purple) 159:10 Solid Block
284 Stained Clay (Blue) 159:11 Solid Block
285 Stained Clay (Brown) 159:12 Solid Block
286 Stained Clay (Green) 159:13 Solid Block
287 Stained Clay (Red) 159:14 Solid Block
288 Stained Clay (Black) 159:15 Solid Block
289 Stained Glass Pane (White) 160 minecraft:stained_glass_pane Solid Block
290 Stained Glass Pane (Orange) 160:1 Solid Block
291 Stained Glass Pane (Magenta) 160:2 Solid Block
292 Stained Glass Pane (Light Blue) 160:3 Solid Block
293 Stained Glass Pane (Yellow) 160:4 Solid Block
294 Stained Glass Pane (Lime) 160:5 Solid Block
295 Stained Glass Pane (Pink) 160:6 Solid Block
296 Stained Glass Pane (Gray) 160:7 Solid Block
297 Stained Glass Pane (Light Gray) 160:8 Solid Block
298 Stained Glass Pane (Cyan) 160:9 Solid Block
299 Stained Glass Pane (Purple) 160:10 Solid Block
300 Stained Glass Pane (Blue) 160:11 Solid Block
301 Stained Glass Pane (Brown) 160:12 Solid Block
302 Stained Glass Pane (Green) 160:13 Solid Block
303 Stained Glass Pane (Red) 160:14 Solid Block
304 Stained Glass Pane (Black) 160:15 Solid Block
305 Acacia Leaves 161 minecraft:leaves2 Solid Block
306 Dark Oak Leaves 161:1 Solid Block
307 Acacia Wood 162 minecraft:log2 Solid Block
308 Dark Oak Wood 162:1 Solid Block
309 Wooden Stairs (Acacia Oak) 163 minecraft:acacia_stairs
310 Wooden Stairs (Dark Oak) 164 minecraft:dark_oak_stairs
311 Slime Block 165 minecraft:slime
312 Barrier 166 minecraft:barrier
313 Iron Trapdoor 167 minecraft:iron_trapdoor
314 Prismarine 168 minecraft:prismarine
315 Prismarine Bricks 168:1
316 Dark Prismarine 168:2
317 Sea Lantern 169 minecraft:sea_lantern
318 Hay Bale 170 minecraft:hay_block
319 Carpet (White) 171 minecraft:carpet
320 Carpet (Orange) 171:1
321 Carpet (Magenta) 171:2
322 Carpet (Light Blue) 171:3
323 Carpet (Yellow) 171:4
324 Carpet (Lime) 171:5
325 Carpet (Pink) 171:6
326 Carpet (Gray) 171:7
327 Carpet (Light Gray) 171:8
328 Carpet (Cyan) 171:9
329 Carpet (Purple) 171:10
330 Carpet (Blue) 171:11
331 Carpet (Brown) 171:12
332 Carpet (Green) 171:13
333 Carpet (Red) 171:14
334 Carpet (Black) 171:15
335 Hardened Clay 172 minecraft:hardened_clay
336 Block of Coal 173 minecraft:coal_block
337 Packed Ice 174 minecraft:packed_ice
338 Sunflower 175 minecraft:double_plant
339 Lilac 175:1
340 Double Tallgrass 175:2
341 Large Fern 175:3
342 Rose Bush 175:4
343 Peony 175:5
344 Free-standing Banner 176 minecraft:standing_banner
345 Wall-mounted Banner 177 minecraft:wall_banner
346 Inverted Daylight Sensor 178 minecraft:daylight_detector_inverted
347 Red Sandstone 179 minecraft:red_sandstone
348 Chiseled Red Sandstone 179:1
349 Smooth Red Sandstone 179:2
350 Red Sandstone Stairs 180 minecraft:red_sandstone_stairs
351 Double Red Sandstone Slab 181 minecraft:double_stone_slab2
352 Red Sandstone Slab 182 minecraft:stone_slab2
353 Spruce Fence Gate 183 minecraft:spruce_fence_gate
354 Birch Fence Gate 184 minecraft:birch_fence_gate
355 Jungle Fence Gate 185 minecraft:jungle_fence_gate
356 Dark Oak Fence Gate 186 minecraft:dark_oak_fence_gate
357 Acacia Fence Gate 187 minecraft:acacia_fence_gate
358 Spruce Fence 188 minecraft:spruce_fence
359 Birch Fence 189 minecraft:birch_fence
360 Jungle Fence 190 minecraft:jungle_fence
361 Dark Oak Fence 191 minecraft:dark_oak_fence
362 Acacia Fence 192 minecraft:acacia_fence
363 Spruce Door Block 193 minecraft:spruce_door
364 Birch Door Block 194 minecraft:birch_door
365 Jungle Door Block 195 minecraft:jungle_door
366 Acacia Door Block 196 minecraft:acacia_door
367 Dark Oak Door Block 197 minecraft:dark_oak_door
368 End Rod 198 minecraft:end_rod
369 Chorus Plant 199 minecraft:chorus_plant
370 Chorus Flower 200 minecraft:chorus_flower
371 Purpur Block 201 minecraft:purpur_block
372 Purpur Pillar 202 minecraft:purpur_pillar
373 Purpur Stairs 203 minecraft:purpur_stairs
374 Purpur Double Slab 204 minecraft:purpur_double_slab
375 Purpur Slab 205 minecraft:purpur_slab
376 End Stone Bricks 206 minecraft:end_bricks
377 Beetroot Block 207 minecraft:beetroots
378 Repeating Command Block 210 minecraft:repeating_command_block
379 Chain Command Block 211 minecraft:chain_command_block
380 Iron Shovel 256 minecraft:iron_shovel
381 Iron Pickaxe 257 minecraft:iron_pickaxe
382 Iron Axe 258 minecraft:iron_axe
383 Flint and Steel 259 minecraft:flint_and_steel
384 Apple 260 minecraft:apple
385 Bow 261 minecraft:bow
386 Arrow 262 minecraft:arrow
387 Coal 263 minecraft:coal
388 Charcoal 263:1
389 Diamond 264 minecraft:diamond
390 Iron Ingot 265 minecraft:iron_ingot
391 Gold Ingot 266 minecraft:gold_ingot
392 Iron Sword 267 minecraft:iron_sword
393 Wooden Sword 268 minecraft:wooden_sword
394 Wooden Shovel 269 minecraft:wooden_shovel
395 Wooden Pickaxe 270 minecraft:wooden_pickaxe
396 Wooden Axe 271 minecraft:wooden_axe
397 Stone Sword 272 minecraft:stone_sword
398 Stone Shovel 273 minecraft:stone_shovel
399 Stone Pickaxe 274 minecraft:stone_pickaxe
400 Stone Axe 275 minecraft:stone_axe
401 Diamond Sword 276 minecraft:diamond_sword
402 Diamond Shovel 277 minecraft:diamond_shovel
403 Diamond Pickaxe 278 minecraft:diamond_pickaxe
404 Diamond Axe 279 minecraft:diamond_axe
405 Stick 280 minecraft:stick
406 Bowl 281 minecraft:bowl
407 Mushroom Stew 282 minecraft:mushroom_stew
408 Gold Sword 283 minecraft:golden_sword
409 Gold Shovel 284 minecraft:golden_shovel
410 Gold Pickaxe 285 minecraft:golden_pickaxe
411 Gold Axe 286 minecraft:golden_axe
412 String 287 minecraft:string
413 Feather 288 minecraft:feather
414 Gunpowder 289 minecraft:gunpowder
415 Wooden Hoe 290 minecraft:wooden_hoe
416 Stone Hoe 291 minecraft:stone_hoe
417 Iron Hoe 292 minecraft:iron_hoe
418 Diamond Hoe 293 minecraft:diamond_hoe
419 Gold Hoe 294 minecraft:golden_hoe
420 Wheat Seeds 295 minecraft:wheat_seeds
421 Wheat 296 minecraft:wheat
422 Bread 297 minecraft:bread
423 Leather Helment 298 minecraft:leather_helmet
424 Leather Tunic 299 minecraft:leather_chestplate
425 Leather Leggings 300 minecraft:leather_leggings
426 Leather Boots 301 minecraft:leather_boots
427 Chainmail Helment 302 minecraft:chainmail_helmet
428 Chainmail Chestplate 303 minecraft:chainmail_chestplate
429 Chainmail Leggings 304 minecraft:chainmail_leggings
430 Chainmail Boots 305 minecraft:chainmail_boots
431 Iron Helment 306 minecraft:iron_helmet
432 Iron Chestplate 307 minecraft:iron_chestplate
433 Iron Leggings 308 minecraft:iron_leggings
434 Iron Boots 309 minecraft:iron_boots
435 Diamond Helment 310 minecraft:diamond_helmet
436 Diamond Chestplate 311 minecraft:diamond_chestplate
437 Diamond Leggings 312 minecraft:diamond_leggings
438 Diamond Boots 313 minecraft:diamond_boots
439 Gold Helment 314 minecraft:golden_helmet
440 Gold Chestplate 315 minecraft:golden_chestplate
441 Gold Leggings 316 minecraft:golden_leggings
442 Gold Boots 317 minecraft:golden_boots
443 Flint 318 minecraft:flint
444 Raw Porkchop 319 minecraft:porkchop
445 Cooked Porkchop 320 minecraft:cooked_porkchop
446 Painting 321 minecraft:painting
447 Golden Apple 322 minecraft:golden_apple
448 Enchanted Golden Apple (Notch Apple) 322:1
449 Sign 323 minecraft:sign
450 Wooden Door 324 minecraft:wooden_door
451 Bucket 325 minecraft:bucket
452 Bucket (Water) 326 minecraft:water_bucket
453 Bucket (Lava) 327 minecraft:lava_bucket
454 Minecart 328 minecraft:minecart
455 Saddle 329 minecraft:saddle
456 Iron Door 330 minecraft:iron_door
457 Redstone Dust 331 minecraft:redstone
458 Snowball 332 minecraft:snowball
459 Boat 333 minecraft:boat
460 Leather 334 minecraft:leather
461 Bucket (Milk) 335 minecraft:milk_bucket
462 Clay Brick 336 minecraft:brick
463 Clay 337 minecraft:clay_ball
464 Sugar Cane 338 minecraft:reeds
465 Paper 339 minecraft:paper
466 Book 340 minecraft:book
467 Slime Ball 341 minecraft:slime_ball
468 Minecart (Storage) (Chest) 342 minecraft:chest_minecart
469 Minecart (Powered) (Furance) 343 minecraft:furnace_minecart
470 Egg 344 minecraft:egg
471 Compass 345 minecraft:compass
472 Fishing Rod 346 minecraft:fishing_rod
473 Clock 347 minecraft:clock
474 Glowstone Dust 348 minecraft:glowstone_dust
475 Raw Fish 349 minecraft:fish
476 Raw Salmon 349:1
477 Clownfish 349:2
478 Pufferfish 349:3
479 Cooked Fish 350 minecraft:cooked_fished
480 Cooked Salmon 350:1
481 Clownfish 350:2
482 Pufferfish 350:3
483 Ink Sack 351 minecraft:dye
484 Rose Red Dye 351:1
485 Cactus Green Dye 351:2
486 Cocoa Bean 351:3
487 Lapis Lazuli 351:4
488 Purple Due 351:5
489 Cyan Dye 351:6
490 Light Gray Dye 351:7
491 Gray Dye 351:8
492 Pink Dye 351:9
493 Lime Dye 351:10
494 Dandelion Yellow Dye 351:11
495 Light Blue Dye 351:12
496 Magenta Dye 351:13
497 Orange Dye 351:14
498 Bone Meal 351:15
499 Bone 352 minecraft:bone
500 Sugar 353 minecraft:sugar
501 Cake 354 minecraft:cake
502 Bed 355 minecraft:bed
503 Redstone Repeater 356 minecraft:repeater
504 Cookie 357 minecraft:cookie
505 Map 358 minecraft:filled_map
506 Shears 359 minecraft:shears
507 Melon (Slice) 360 minecraft:melon
508 Pumplin Seeds 361 minecraft:pumpkin_seeds
509 Melon Seeds 362 minecraft:melon_seeds
510 Raw Beef 363 minecraft:beef
511 Steak 364 minecraft:cooked_beef
512 Raw Chicken 365 minecraft:chicken
513 Cooked Chicken 366 minecraft:cooked_chicken
514 Rotten Flesh 367 minecraft:rotten_flesh
515 Ender Pearl 368 minecraft:ender_pearl
516 Blaze Rod 369 minecraft:blaze_rod
517 Ghast Tear 370 minecraft:ghast_tear
518 Gold Nugget 371 minecraft:god_nugget
519 Nether Wart 372 minecraft:nether_wart
520 Water Bottle 373 minecraft:potion Potion
521 Awkward Potion 373:16 Potion
522 Thick Potion 373:32 Potion
523 Mundane Potion 373:64 Potion
524 Regeneration Potion (0:45) 373:8193 Potion
525 Swiftness Potion (3:00) 373:8194 Potion
526 Fire Resistance Potion (3:00) 373:8195 Potion
527 Poison Potion (0:45) 373:8196 Potion
528 Healing Potion 373:8197 Potion
529 Night Vision Potion (3:00) 373:8198 Potion
530 Weakness Potion (1:30) 373:8200 Potion
531 Strength Potion (3:00) 373:8201 Potion
532 Slowness Potion (1:30) 373:8202 Potion
533 Harming Potion 373:8204 Potion
534 Water Breathing Potion (3:00) 373:8205 Potion
535 Invisibility Potion (3:00) 373:8206 Potion
536 Regeneration Potion II (0:22) 373:8225 Potion
537 Swiftness Potion II (1:30) 373:8226 Potion
538 Poison Potion II (0:22) 373:8228 Potion
539 Healing Potion II 373:8229 Potion
540 Strength Potion II (1:30) 373:8233 Potion
541 Harming Potion II 373:8236 Potion
542 Regeneration Potion (2:00) 373:8257 Potion
543 Swiftness Potion (8:00) 373:8258 Potion
544 Fire Resistance Potion (8:00) 373:8259 Potion
545 Poison Potion (2:00) 373:8260 Potion
546 Night Vision Potion (8:00) 373:8262 Potion
547 Weakness Potion (4:00) 373:8264 Potion
548 Strength Potion (8:00) 373:8265 Potion
549 Slowness Potion (4:00) 373:8266 Potion
550 Water Breathing Potion (8:00) 373:8269 Potion
551 Invisbility Potion (8:00) 373:8270 Potion
552 Regeneration Potion II (1:00) 373:8289 Potion
553 Swiftness Potion II (4:00) 373:8290 Potion
554 Poison Potion II (1:00) 373:8292 Potion
555 Strength Potion II (4:00) 373:8297 Potion
556 Regeneration Splash (0:33) 373:16385 Potion
557 Swiftness Splash (2:15) 373:16386 Potion
558 Fire Resistance Splash (2:15) 373:16387 Potion
559 Poison Splash (0:33) 373:16288 Potion
560 Healing Splash 373:16389 Potion
561 Night Vision Splash (2:15) 373:16390 Potion
562 Weakness Splash (1:07) 373:16392 Potion
563 Strength Splash (2:15) 373:16393 Potion
564 Slowness Splash (1:07) 373:16394 Potion
565 Harming Splash 373:16396 Potion
566 Water Breathing Splash (2:15) 373:16197 Potion
567 Invisbility Splash (2:15) 373:16338 Potion
568 Regeneration Splash II (0:16) 373:16417 Potion
569 Swiftness Splash II (1:07) 373:16418 Potion
570 Poison Splash II (0:16) 373:16420 Potion
571 Healing Splash II 373:16421 Potion
572 Strength Splash II (1:07) 373:16425 Potion
573 Harming Splash II 373:16428 Potion
574 Regeneration Splash (1:30) 373:16449 Potion
575 Swiftness Splash (6:00) 373:16450 Potion
576 Fire Resistance Splash (6:00) 373:16451 Potion
577 Poison Splash (1:30) 373:16452 Potion
578 Night Vision Splash (6:00) 373:16454 Potion
579 Weakness Splash (3:00) 373:16456 Potion
580 Strength Splash (6:00) 373:16457 Potion
581 Slowness Splash (3:00) 373:16458 Potion
582 Water Breathing Splash (6:00) 373:16461 Potion
583 Invisbility Splash (6:00) 373:16462 Potion
584 Regeneration Splash II (0:45) 373:16481 Potion
585 Swiftness Splash II (3:00) 373:16482 Potion
586 Poison Splash II (0:45) 373:16484 Potion
587 Strength Splash II (3:00) 373:16489 Potion
588 Glass Bottle 374 minecraft:glass_bottle
589 Spider Eye 375 minecraft:spider_eye
590 Fermented Spider Eye 376 minecraft:fermented_spider_eye
591 Blaze Powder 377 minecraft:blaze_powder
592 Magma Cream 378 minecraft:magma_cream
593 Brewing Stand 379 minecraft:brewing_stand
594 Cauldron 380 minecraft:cauldron
595 Eye of Ender 381 minecraft:ender_eye
596 Glistering Melon 382 minecraft:speckled_melon
597 Spawn Egg (Creeper) 383:50 minecraft:spawn_egg Item
598 Spawn Egg (Skeleton) 383:51 Item
599 Spawn Egg (Spider) 383:52 Item
600 Spawn Egg (Zombie) 383:54 Item
601 Spawn Egg (Slime) 383:55 Item
602 Spawn Egg (Ghast) 383:56 Item
603 Spawn Egg (Zombie Pigmen) 383:57 Item
604 Spawn Egg (Endermen) 383:58 Item
605 Spawn Egg (Cave Spider) 383:59 Item
606 Spawn Egg (Silverfish) 383:60 Item
607 Spawn Egg (Blaze) 383:61 Item
608 Spawn Egg (Magma Cube) 383:62 Item
609 Spawn Egg (Bat) 383:65 Item
610 Spawn Egg (Witch) 383:66 Item
611 Spawn Egg (Pig) 383:90 Item
612 Spawn Egg (Sheep) 383:91 Item
613 Spawn Egg (Cow) 383:92 Item
614 Spawn Egg (Chicken) 383:93 Item
615 Spawn Egg (Squid) 383:94 Item
616 Spawn Egg (Wolf) 383:95 Item
617 Spawn Egg (Mooshroom) 383:96 Item
618 Spawn Egg (Ocelot) 383:98 Item
619 Spawn Egg (Horse) 383:100 Item
620 Spawn Egg (Villager) 383:120 Item
621 Bottle o' Enchanting 384 minecraft:experience_bottle
622 Fire Charge 385 minecraft:fire_charge
623 Book and Quill 386 minecraft:writable_book
624 Written Book 387 minecraft:written_book
625 Emerald 388 minecraft:emerald
626 Item Frame 389 minecraft:item_frame
627 Flower Pot 390 minecraft:flower_pot
628 Carrot 391 minecraft:carrot
629 Potato 392 minecraft:potato
630 Baked Potato 393 minecraft:baked_potato
631 Poisonous Potato 394 minecraft:poisonous_potato
632 Empty Map 395 minecraft:map
633 Golden Carrot 396 minecraft:golden_carrot
634 Head (Skeleton) 397 minecraft:skull
635 Head (Wither) 397:1
636 Head (Zombie) 397:2
637 Head (Steve) 397:3
638 Head (Creeper) 397:4
639 Carrot on a Stick 398 minecraft:carrot_on_a_stick
640 Nether Star 399 minecraft:nether_star
641 Pumpkin Pie 400 minecraft:pumpkin_pie
642 Firework Rocket 401 minecraft:fireworks
643 Firework Star 402 minecraft:firework_charge
644 Enchanted Book 403 minecraft:enchanted_book
645 Redstone Comparator 404 minecraft:comparator
646 Nether Brick (Item) 405 minecraft:netherbrick
647 Nether Quartz 406 minecraft:quartz
648 Minecart (TNT) 407 minecraft:tnt_minecart
649 Minecart (Hopper) 408 minecraft:hopper_minecart
650 Iron Horse Armor 417 minecraft:iron_horse_armor
651 Gold Horse Armor 418 minecraft:golden_horse_armor
652 Diamond Horse Armor 419 minecraft:diamond_horse_armor
653 Lead 420 minecraft:lead
654 Name Tag 421 minecraft:name_tag
655 Minecart (Command Block) 422 minecraft:command_block_minecart
656 Music Disk (13) 2256 minecraft:record_13 Decorations
657 Music Disk (Cat) 2257 minecraft:record_cat Decorations
658 Music Disk (Blocks) 2258 minecraft:record_blocks Decorations
659 Music Disk (Chrip) 2259 minecraft:record_chirp Decorations
660 Music Disk (Far) 2260 minecraft:record_far Decorations
661 Music Disk (Mall) 2261 minecraft:record_mall Decorations
662 Music Disk (Mellohi) 2262 minecraft:record_mellohi Decorations
663 Music Disk (Stal) 2263 minecraft:record_stal Decorations
664 Music Disk (Strad) 2264 minecraft:record_strad Decorations
665 Music Disk (Ward) 2265 minecraft:record_ward Decorations
666 Music Disk (11) (Broken) 2266 minecraft:record_11 Decorations
667 Music Disk (Wait) 2267 minecraft:record_wait Decorations

View File

@@ -41,6 +41,7 @@ function nativefs.list(node, dir, full)
end
if not files then
print(dir)
error('Not a directory')
end

View File

@@ -18,9 +18,9 @@ process:newThread('trust_server', function()
else
data = Crypto.decrypt(data, password)
if data and data.pk and data.dh == socket.dhost then
local trustList = Util.readTable('.known_hosts') or { }
local trustList = Util.readTable('usr/.known_hosts') or { }
trustList[data.dh] = data.pk
Util.writeTable('.known_hosts', trustList)
Util.writeTable('usr/.known_hosts', trustList)
socket:write({ success = true, msg = 'Trust accepted' })
else