Rewrite entire mod
Now far more efficient, and prepared for the addition of other computer-related items and computers.
This commit is contained in:
parent
78b1b836a7
commit
de79747243
@ -1,8 +0,0 @@
|
||||
-- bios os config
|
||||
clear = "clear" -- clear command
|
||||
off = "shutdown" -- shutdown command
|
||||
reboot = "reboot" -- reboot command
|
||||
digiline = false -- do not support digilines
|
||||
network = false -- do not support network
|
||||
on = "rightclick" -- on command (rightclick)
|
||||
clear_on_close = false -- do not clear output on close
|
@ -1,2 +1,12 @@
|
||||
set_output(get_output()..get_field("input").."\n"..get_attr("name")..":~$ ") -- print input
|
||||
refresh() -- refresh
|
||||
local input = get_attr("input")
|
||||
|
||||
if input ~= "" then
|
||||
print(input, false) -- print input
|
||||
|
||||
local ok, res = run(input)
|
||||
if res then print(res) end
|
||||
|
||||
print(get_os("prefix")) -- Print prefix
|
||||
set_input("")
|
||||
refresh() -- refresh
|
||||
end
|
||||
|
@ -1 +1,11 @@
|
||||
set_output("Welcome to BiosOS version 0.1.\n\n"..get_attr("name")..":~$ ") -- print welcome
|
||||
-- Set OS values
|
||||
set_os("clear", "clear")
|
||||
set_os("off", "shutdown")
|
||||
set_os("reboot", "shutdown -r")
|
||||
set_os("prefix", get_attr("name")..":~$ ")
|
||||
|
||||
-- Set initial output value
|
||||
set_output("Welcome to BiosOS version 0.1.\n\n"..get_os("prefix")) -- print welcome
|
||||
|
||||
-- Refresh view
|
||||
refresh()
|
||||
|
123
builtin.lua
Normal file
123
builtin.lua
Normal file
@ -0,0 +1,123 @@
|
||||
-- digicompute/builtin.lua
|
||||
|
||||
digicompute.builtin = {}
|
||||
local builtin = digicompute.builtin
|
||||
|
||||
-- [function] check if file exists
|
||||
function builtin.exists(path)
|
||||
local f = io.open(path, "r") -- open file
|
||||
if f ~= nil then f:close() return true end
|
||||
end
|
||||
|
||||
-- [function] list contents
|
||||
function builtin.list(path)
|
||||
local files = minetest.get_dir_list(path, false)
|
||||
local subdirs = minetest.get_dir_list(path, true)
|
||||
|
||||
return {
|
||||
files = files or nil,
|
||||
subdirs = subdirs or nil,
|
||||
}
|
||||
end
|
||||
|
||||
-- [function] create file
|
||||
function builtin.create(path)
|
||||
local f = io.open(path, "w") -- create file
|
||||
f:close() -- close file
|
||||
return true
|
||||
end
|
||||
|
||||
-- [function] write to file
|
||||
function builtin.write(path, data, mode)
|
||||
if mode ~= "w" and mode ~= "a" then
|
||||
mode = "w"
|
||||
end
|
||||
local f = io.open(path, mode) -- open file for writing
|
||||
f:write(data) -- write data
|
||||
f:close() -- close file
|
||||
return true
|
||||
end
|
||||
|
||||
-- [function] read file
|
||||
function builtin.read(path)
|
||||
local f = io.open(path, "r") -- open file for reading
|
||||
if f then
|
||||
local data = f:read("*all") -- read and store all data
|
||||
return data -- return file contents
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] copy file
|
||||
function builtin.copy(original, new)
|
||||
local original = builtin.read(original) -- read
|
||||
if original then
|
||||
builtin.write(new, original) -- write
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] create directory
|
||||
function builtin.mkdir(path)
|
||||
if not io.open(path) then
|
||||
if minetest.mkdir then
|
||||
minetest.mkdir(path) -- create directory if minetest.mkdir is available
|
||||
else
|
||||
os.execute('mkdir "'..path..'"') -- create directory with os mkdir command
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] remove directory
|
||||
function builtin.rmdir(path)
|
||||
if io.open(path) then
|
||||
-- [local function] remove files
|
||||
local function rm_files(ppath, files)
|
||||
for _, f in ipairs(files) do
|
||||
os.remove(ppath.."/"..f)
|
||||
end
|
||||
end
|
||||
|
||||
-- [local function] check and rm dir
|
||||
local function rm_dir(dpath)
|
||||
local files = minetest.get_dir_list(dpath, false)
|
||||
local subdirs = minetest.get_dir_list(dpath, true)
|
||||
rm_files(dpath, files)
|
||||
if subdirs then
|
||||
for _, d in ipairs(subdirs) do
|
||||
rm_dir(dpath.."/"..d)
|
||||
end
|
||||
end
|
||||
os.remove(dpath)
|
||||
end
|
||||
|
||||
rm_dir(path)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] copy directory
|
||||
function builtin.cpdir(original, new)
|
||||
if io.open(original) then
|
||||
-- [local function] copy files
|
||||
local function copy_files(opath, npath, files)
|
||||
for _, f in ipairs(files) do
|
||||
builtin.copy(opath.."/"..f, npath.."/"..f)
|
||||
end
|
||||
end
|
||||
|
||||
-- [local function] check and copy dir
|
||||
local function copy_dir(opath, npath)
|
||||
builtin.mkdir(npath)
|
||||
local files = minetest.get_dir_list(opath, false)
|
||||
local subdirs = minetest.get_dir_list(opath, true)
|
||||
copy_files(opath, npath, files)
|
||||
for _, d in ipairs(subdirs) do
|
||||
copy_dir(opath.."/"..d, npath.."/"..d)
|
||||
end
|
||||
end
|
||||
|
||||
copy_dir(original, new)
|
||||
return true
|
||||
end
|
||||
end
|
690
c_api.lua
690
c_api.lua
@ -1,136 +1,606 @@
|
||||
-- digicompute/c_api.lua
|
||||
--[[
|
||||
API for registering computer nodes. Documentation in progress.
|
||||
]]
|
||||
local modpath = digicompute.modpath
|
||||
local path = digicompute.path
|
||||
|
||||
function digicompute.register_computer(termstring, desc)
|
||||
digicompute.c = {}
|
||||
|
||||
local modpath = digicompute.modpath
|
||||
local path = digicompute.path
|
||||
local main_path = path.."computers/"
|
||||
|
||||
-- Make computer directory
|
||||
digicompute.builtin.mkdir(main_path)
|
||||
|
||||
-------------------
|
||||
-- ID MANAGEMENT --
|
||||
-------------------
|
||||
|
||||
local computers = {}
|
||||
|
||||
-- [function] load computers
|
||||
function digicompute.load_computers()
|
||||
local data = minetest.deserialize(digicompute.builtin.read(path.."/computers.txt"))
|
||||
if type(data) == "table" then
|
||||
computers = data
|
||||
end
|
||||
end
|
||||
|
||||
-- Load all computers
|
||||
digicompute.load_computers()
|
||||
|
||||
-- [function] save computers
|
||||
function digicompute.save_computers()
|
||||
digicompute.builtin.write(path.."/computers.txt", minetest.serialize(computers))
|
||||
end
|
||||
|
||||
-- [function] generate new computer ID
|
||||
function digicompute.c:new_id(pos)
|
||||
assert(type(pos) == "table", "digicompute.c:new_id requires a valid position")
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
local function count()
|
||||
local count = 1
|
||||
for _, i in pairs(computers) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local id = "c_"..count()
|
||||
|
||||
computers[id] = {
|
||||
pos = pos,
|
||||
}
|
||||
|
||||
meta:set_string("id", id)
|
||||
end
|
||||
|
||||
-- [event] save computers on shutdown
|
||||
minetest.register_on_shutdown(digicompute.save_computers)
|
||||
|
||||
-------------------
|
||||
---- FORMSPECS ----
|
||||
-------------------
|
||||
|
||||
local computer_contexts = {}
|
||||
|
||||
local tabs = {
|
||||
"main",
|
||||
"settings",
|
||||
}
|
||||
|
||||
-- [function] handle tabs
|
||||
function digicompute.c:handle_tabs(pos, player, fields)
|
||||
if fields.tabs then
|
||||
if digicompute.c:open(pos, player, tabs[tonumber(fields.tabs)]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
digicompute.c.forms = {
|
||||
naming = {
|
||||
cache_formname = false,
|
||||
get = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
return
|
||||
"size[6,1.7]"..
|
||||
default.gui_bg_img..
|
||||
"field[.25,0.50;6,1;name;Computer Name:;"..minetest.formspec_escape(meta:get_string("name")).."]"..
|
||||
"button[4.95,1;1,1;submit_name;Set]"
|
||||
end,
|
||||
handle = function(pos, player, fields)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = player:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
if owner == name then
|
||||
if fields.name or fields.key_enter_field == "name" and fields.name ~= "" then
|
||||
meta:set_string("name", fields.name)
|
||||
meta:set_string("setup", "true")
|
||||
meta:set_string("path", main_path..meta:get_string("owner").."/"..meta:get_string("id").."/")
|
||||
digicompute.c:init(pos)
|
||||
digicompute.c:open(pos, player)
|
||||
else
|
||||
minetest.chat_send_player(name, "Name cannot be empty.")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Only the owner can set this computer. ("..owner..")")
|
||||
end
|
||||
end,
|
||||
},
|
||||
main = {
|
||||
get = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local input = minetest.formspec_escape(meta:get_string("input"))
|
||||
local help = minetest.formspec_escape(meta:get_string("help"))
|
||||
local output = meta:get_string("output"):split("\n", true)
|
||||
|
||||
for i, line in ipairs(output) do
|
||||
output[i] = minetest.formspec_escape(line)
|
||||
end
|
||||
|
||||
return
|
||||
"size[10,11]"..
|
||||
"tabheader[0,0;tabs;Command Line,Settings;1]"..
|
||||
"bgcolor[#000000FF;]"..
|
||||
"tableoptions[background=#000000FF;highlight=#00000000;border=false]"..
|
||||
"table[-0.25,-0.38;10.38,11.17;list_credits;"..table.concat(output, ",")..";"..#output.."]"..
|
||||
"button[9.56,10.22;0.8,2;help;?]"..
|
||||
"tooltip[help;"..help.."]"..
|
||||
"field[-0.02,10.99;10.1,1;input;;"..input.."]"..
|
||||
"field_close_on_enter[input;false]"
|
||||
end,
|
||||
handle = function(pos, player, fields)
|
||||
if digicompute.c:handle_tabs(pos, player, fields) then return end
|
||||
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
local os = minetest.deserialize(meta:get_string("os")) or {}
|
||||
local prefix = os.prefix or ""
|
||||
|
||||
if fields.input or fields.key_enter_field == "name" then
|
||||
if fields.input == os.clear then
|
||||
meta:set_string("output", prefix)
|
||||
meta:set_string("input", "")
|
||||
digicompute.c:open(pos, player)
|
||||
elseif fields.input == os.off then digicompute.c:off(pos, player)
|
||||
elseif fields.input == os.reboot then digicompute.c:reboot(pos, player)
|
||||
else -- else, turn over to os
|
||||
-- Set meta value(s)
|
||||
meta:set_string("input", fields.input)
|
||||
|
||||
-- Run main.lua
|
||||
digicompute.c:run_file(pos, player, "os/main.lua") -- Run main
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
settings = {
|
||||
get = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
return
|
||||
"size[10,11]"..
|
||||
"tabheader[0,0;tabs;Command Line,Settings;2]"..
|
||||
default.gui_bg_img..
|
||||
"button[0.5,0.25;9,1;reset;Reset Filesystem]"..
|
||||
"tooltip[reset;Wipes all files and OS data replacing it with the basic BiosOS.]"..
|
||||
"label[0.5,10.35;digicompute Version: "..tostring(digicompute.VERSION)..", "..
|
||||
digicompute.RELEASE_TYPE.."]"..
|
||||
"label[0.5,10.75;(c) Copywrite "..tostring(os.date("%Y")).." "..
|
||||
"Elijah Duffy <theoctacian@gmail.com>]"
|
||||
end,
|
||||
handle = function(pos, player, fields)
|
||||
if digicompute.c:handle_tabs(pos, player, fields) then return end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if fields.reset then
|
||||
-- Clear buffers
|
||||
meta:set_string("output", "")
|
||||
meta:set_string("input", "")
|
||||
|
||||
-- Reset Filesystem
|
||||
digicompute.c:reinit(pos)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
-- [function] open formspec
|
||||
function digicompute.c:open(pos, player, formname)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if meta:get_string("setup") == "true" then
|
||||
local meta_formname = meta:get_string("formname")
|
||||
|
||||
if not formname and meta_formname and meta_formname ~= "" then
|
||||
formname = meta_formname
|
||||
end
|
||||
else
|
||||
formname = "naming"
|
||||
end
|
||||
|
||||
formname = formname or "main"
|
||||
local form = digicompute.c.forms[formname]
|
||||
|
||||
if form then
|
||||
local name = player:get_player_name()
|
||||
|
||||
if form.cache_formname ~= false then
|
||||
meta:set_string("formname", formname)
|
||||
end
|
||||
|
||||
computer_contexts[name] = minetest.get_meta(pos):get_string("id")
|
||||
minetest.show_formspec(name, "digicompute:"..formname, form.get(pos, player))
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [event] on receive fields
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local formname = formname:split(":")
|
||||
|
||||
if formname[1] == "digicompute" and digicompute.c.forms[formname[2]] then
|
||||
local computer = computers[computer_contexts[player:get_player_name()]]
|
||||
|
||||
if computer then
|
||||
local pos = computer.pos
|
||||
minetest.get_meta(pos):set_string("current_user", player:get_player_name())
|
||||
digicompute.c.forms[formname[2]].handle(pos, player, fields)
|
||||
else
|
||||
minetest.chat_send_player(player:get_player_name(), "Computer could not be found!")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------
|
||||
-- HELPER FUNCTIONS --
|
||||
----------------------
|
||||
|
||||
-- [function] update infotext
|
||||
function digicompute.c:infotext(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local state = minetest.registered_nodes[minetest.get_node(pos).name].digicompute.state
|
||||
|
||||
if meta:get_string("setup") == "true" then
|
||||
meta:set_string("infotext", meta:get_string("name").." - "..state.."\n(owned by "
|
||||
..meta:get_string("owner")..")")
|
||||
else
|
||||
meta:set_string("infotext", "Unconfigured Computer - "..state.."\n(owned by "
|
||||
..meta:get_string("owner")..")")
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] initialize computer
|
||||
function digicompute.c:init(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local path = meta:get_string("path")
|
||||
|
||||
if path and path ~= "" then
|
||||
digicompute.builtin.mkdir(main_path..meta:get_string("owner"))
|
||||
digicompute.builtin.mkdir(path)
|
||||
digicompute.builtin.cpdir(digicompute.modpath.."/bios/", path.."os")
|
||||
digicompute.c:run_file(pos, meta:get_string("owner"), "os/start.lua")
|
||||
digicompute.log("Initialized computer "..meta:get_string("id").." owned by "..
|
||||
meta:get_string("owner").." at "..minetest.pos_to_string(pos))
|
||||
digicompute.c:infotext(pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] deinitialize computer
|
||||
function digicompute.c:deinit(pos, clear_entry)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local path = meta:get_string("path")
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
if path and path ~= "" then
|
||||
digicompute.builtin.rmdir(path)
|
||||
digicompute.log("Deinitialized computer "..meta:get_string("id").." owned by "..
|
||||
meta:get_string("owner").." at "..minetest.pos_to_string(pos))
|
||||
|
||||
if digicompute.builtin.list(main_path..owner).subdirs then
|
||||
os.remove(main_path..owner)
|
||||
end
|
||||
end
|
||||
|
||||
if clear_entry ~= false then
|
||||
local id = meta:get_string("id")
|
||||
computers[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] reinitialize computer (reset)
|
||||
function digicompute.c:reinit(pos)
|
||||
digicompute.c:deinit(pos, false)
|
||||
digicompute.c:init(pos)
|
||||
end
|
||||
|
||||
-- [function] turn computer on
|
||||
function digicompute.c:on(pos, player)
|
||||
local temp = minetest.get_node(pos)
|
||||
local ddef = minetest.registered_nodes[temp.name].digicompute
|
||||
if ddef.state == "off" then
|
||||
local name, param2 = temp.name, temp.param2
|
||||
|
||||
-- Swap to Bios
|
||||
minetest.swap_node({x = pos.x, y = pos.y, z = pos.z}, {name = name.."_bios", param2 = param2}) -- set node to bios
|
||||
|
||||
-- Update infotext
|
||||
digicompute.c:infotext(pos)
|
||||
|
||||
-- Swap to on node after 2 seconds
|
||||
minetest.after(2, function(pos_)
|
||||
minetest.swap_node({x = pos_.x, y = pos_.y, z = pos_.z}, {name = name.."_on", param2 = param2})
|
||||
|
||||
-- Update infotext
|
||||
digicompute.c:infotext(pos)
|
||||
-- Run start if setup
|
||||
if minetest.get_meta(pos):get_string("setup") == "true" then
|
||||
digicompute.c:run_file(pos, player, "os/start.lua")
|
||||
end
|
||||
end, vector.new(pos))
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] turn computer off
|
||||
function digicompute.c:off(pos, player)
|
||||
local temp = minetest.get_node(pos) -- Get basic node information
|
||||
local offname = "digicompute:"..minetest.registered_nodes[temp.name].digicompute.base
|
||||
-- Swap node to off
|
||||
minetest.swap_node({x = pos.x, y = pos.y, z = pos.z}, {name = offname, param2 = temp.param2})
|
||||
-- Update infotext
|
||||
digicompute.c:infotext(pos)
|
||||
-- Update Formspec
|
||||
minetest.close_formspec(player:get_player_name(), "")
|
||||
-- Clear update buffer
|
||||
minetest.get_meta(pos):set_string("output", "")
|
||||
end
|
||||
|
||||
-- [function] reboot computer
|
||||
function digicompute.c:reboot(pos, player)
|
||||
digicompute.c:off(pos, player)
|
||||
digicompute.c:on(pos, player)
|
||||
end
|
||||
|
||||
-----------------------
|
||||
----- ENVIRONMENT -----
|
||||
-----------------------
|
||||
|
||||
-- [function] Make environment
|
||||
function digicompute.c:make_env(pos, player)
|
||||
assert(pos, "digicompute.c:make_env missing position")
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
-- Main Environment Functions
|
||||
|
||||
local main = {}
|
||||
|
||||
-- [local function] print
|
||||
function main.print(contents, newline)
|
||||
if type(contents) ~= "string" then
|
||||
contents = dump(contents)
|
||||
end
|
||||
if newline == false then
|
||||
newline = ""
|
||||
else
|
||||
newline = "\n"
|
||||
end
|
||||
|
||||
meta:set_string("output", meta:get_string("output")..newline..contents)
|
||||
end
|
||||
-- [local function] set help
|
||||
function main.set_help(value)
|
||||
if not value or type(value) ~= "string" then
|
||||
value = "Type a command and press enter."
|
||||
end
|
||||
|
||||
return meta:set_string("help", value)
|
||||
end
|
||||
-- [local function] get attribute
|
||||
function main.get_attr(key)
|
||||
return meta:get_string(key) or nil
|
||||
end
|
||||
-- [local function] get output
|
||||
function main.get_output()
|
||||
return meta:get_string("output") or nil
|
||||
end
|
||||
-- [local function] set output
|
||||
function main.set_output(value)
|
||||
return meta:set_string("output", value)
|
||||
end
|
||||
-- [local function] get input
|
||||
function main.get_input()
|
||||
return meta:get_string("input") or nil
|
||||
end
|
||||
-- [local function] set input
|
||||
function main.set_input(value)
|
||||
return meta:set_string("input", value)
|
||||
end
|
||||
-- [local function] get os value
|
||||
function main.get_os(key)
|
||||
return minetest.deserialize(meta:get_string("os"))[key] or nil
|
||||
end
|
||||
-- [local function] set os value
|
||||
function main.set_os(key, value)
|
||||
local allowed_keys = {
|
||||
clear = true,
|
||||
off = true,
|
||||
reboot = true,
|
||||
prefix = true,
|
||||
}
|
||||
|
||||
if allowed_keys[key] == true then
|
||||
local table = minetest.deserialize(meta:get_string("os")) or {}
|
||||
table[key] = value
|
||||
return meta:set_string("os", minetest.serialize(table))
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- [local function] get userdata value
|
||||
function main.get_userdata(key)
|
||||
return minetest.deserialize(meta:get_string("userdata"))[key] or nil
|
||||
end
|
||||
-- [local function] set userdata value
|
||||
function main.set_userdata(key, value)
|
||||
local table = minetest.deserialize(meta:get_string("userdata")) or {}
|
||||
table[key] = value
|
||||
return meta:set_string("userdata", minetest.serialize(table))
|
||||
end
|
||||
-- [local function] refresh
|
||||
function main.refresh()
|
||||
return digicompute.c:open(pos, minetest.get_player_by_name(meta:get_string("current_user")))
|
||||
end
|
||||
-- [local function] run code
|
||||
function main.run(code)
|
||||
return digicompute.c:run_code(pos, player, code)
|
||||
end
|
||||
|
||||
-- Filesystem Environment Functions
|
||||
|
||||
local fs = {}
|
||||
local cpath = meta:get_string("path")
|
||||
|
||||
-- [local function] exists
|
||||
function fs.exists(path)
|
||||
return digicompute.builtin.exists(cpath..path)
|
||||
end
|
||||
-- [local function] create file
|
||||
function fs.create(path)
|
||||
return digicompute.builtin.create(cpath..path)
|
||||
end
|
||||
-- [local function] remove file
|
||||
function fs.remove(path)
|
||||
return os.remove(cpath..path)
|
||||
end
|
||||
-- [local function] write to file
|
||||
function fs.write(path, data, mode)
|
||||
if type(data) ~= "string" then
|
||||
data = dump(data)
|
||||
end
|
||||
return digicompute.builtin.write(cpath..path, data, mode)
|
||||
end
|
||||
-- [local function] read file
|
||||
function fs.read(path)
|
||||
return digicompute.builtin.read(cpath..path)
|
||||
end
|
||||
-- [local function] list directory contents
|
||||
function fs.list(path)
|
||||
return digicompute.builtin.list(cpath..path)
|
||||
end
|
||||
-- [local function] copy file
|
||||
function fs.copy(original, new)
|
||||
return digicompute.builtin.copy(cpath..original, cpath..new)
|
||||
end
|
||||
-- [local function] create directory
|
||||
function fs.mkdir(path)
|
||||
return digicompute.builtin.mkdir(cpath..path)
|
||||
end
|
||||
-- [local function] remove directory
|
||||
function fs.rmdir(path)
|
||||
return digicompute.builtin.rmdir(cpath..path)
|
||||
end
|
||||
-- [local function] copy directory
|
||||
function fs.cpdir(original, new)
|
||||
return digicompute.builtin.cpdir(cpath..original, cpath..new)
|
||||
end
|
||||
-- [local function] run file
|
||||
function fs.run(path)
|
||||
return digicompute.c:run_file(pos, player, path)
|
||||
end
|
||||
|
||||
-- Get default env table
|
||||
|
||||
local env = digicompute.env()
|
||||
|
||||
env.fs = fs
|
||||
|
||||
for k, v in pairs(main) do
|
||||
env[k] = v
|
||||
end
|
||||
|
||||
return env
|
||||
end
|
||||
|
||||
-- [function] run code
|
||||
function digicompute.c:run_code(pos, player, code)
|
||||
local env = digicompute.c:make_env(pos, player)
|
||||
local ok, res = digicompute.run_code(code, env)
|
||||
return ok, res
|
||||
end
|
||||
|
||||
-- [function] run file
|
||||
function digicompute.c:run_file(pos, player, path)
|
||||
local path = minetest.get_meta(pos):get_string("path")..path
|
||||
local env = digicompute.c:make_env(pos, player)
|
||||
local ok, res = digicompute.run_file(path, env)
|
||||
return ok, res
|
||||
end
|
||||
|
||||
----------------------
|
||||
-- NODE DEFINITIONS --
|
||||
----------------------
|
||||
|
||||
function digicompute.register_computer(itemstring, def)
|
||||
-- off
|
||||
minetest.register_node("digicompute:"..termstring, {
|
||||
minetest.register_node("digicompute:"..itemstring, {
|
||||
digicompute = {
|
||||
state = "off",
|
||||
base = itemstring,
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
description = desc.description,
|
||||
tiles = desc.off_tiles,
|
||||
description = def.description,
|
||||
tiles = def.off_tiles,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky = 2},
|
||||
drop = "digicompute:"..termstring,
|
||||
drop = "digicompute:"..itemstring,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
node_box = desc.node_box,
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, node, channel, msg)
|
||||
if digicompute.digiline ~= false then
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
-- if channel is correct, turn on
|
||||
if channel == meta:get_string("channel") then
|
||||
if msg.system == digicompute.digiline_on then
|
||||
digicompute.on(pos, termstring)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
},
|
||||
},
|
||||
after_place_node = function(pos, placer)
|
||||
node_box = def.node_box,
|
||||
after_place_node = function(pos, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("owner", player:get_player_name())
|
||||
meta:set_string("input", "") -- Initialize input buffer
|
||||
meta:set_string("output", "") -- Initialize output buffer
|
||||
meta:set_string("os", "") -- Initialize OS table
|
||||
meta:set_string("userspace", "") -- Initialize userspace table
|
||||
meta:set_string("help", "Type a command and press enter.") -- Initialize help
|
||||
digicompute.c:new_id(pos) -- Set up ID
|
||||
|
||||
-- Update infotext
|
||||
digicompute.c:infotext(pos)
|
||||
end,
|
||||
on_rightclick = function(pos)
|
||||
digicompute.on(pos, termstring)
|
||||
on_rightclick = function(pos, node, player)
|
||||
digicompute.c:on(pos, player)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
if meta:get_string("name") then digicompute.fs.deinit(pos) end
|
||||
if minetest.get_meta(pos):get_string("name") then
|
||||
digicompute.c:deinit(pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
-- bios
|
||||
minetest.register_node("digicompute:"..termstring.."_bios", {
|
||||
minetest.register_node("digicompute:"..itemstring.."_bios", {
|
||||
digicompute = {
|
||||
state = "bios",
|
||||
base = itemstring,
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
description = desc.description,
|
||||
tiles = desc.bios_tiles,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drop = "",
|
||||
groups = {unbreakable = 1, not_in_creative_inventory = 1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
node_box = desc.node_box,
|
||||
})
|
||||
-- on
|
||||
minetest.register_node("digicompute:"..termstring.."_on", {
|
||||
drawtype = "nodebox",
|
||||
description = desc.description,
|
||||
tiles = desc.on_tiles,
|
||||
defription = def.defription,
|
||||
tiles = def.bios_tiles,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky = 2, not_in_creative_inventory = 1},
|
||||
drop = "digicompute:"..termstring,
|
||||
drop = "digicompute:"..itemstring,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
node_box = desc.node_box,
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, node, channel, msg)
|
||||
-- if os supports digilines and digiline on, listen for signal
|
||||
if digicompute.digiline ~= false and digicompute.on:find("digiline") then
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
if channel ~= meta:get_string("channel") then return end -- ignore if not proper channel
|
||||
if msg.system then
|
||||
if msg.system == digicompute.clear then digicompute.clear("output", pos) -- clear output
|
||||
elseif msg.system == digicompute.off then digicompute.off(pos, termstring) -- turn off
|
||||
elseif msg.system == digicompute.reboot then digicompute.reboot(pos, termstring) -- reboot
|
||||
else digicompute.proc_digiline({x = pos.x, y = pos.y, z = pos.z}, fields.input) end -- else, hand over to OS
|
||||
end
|
||||
end
|
||||
end
|
||||
},
|
||||
},
|
||||
on_construct = function(pos) -- set meta and formspec
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
meta:set_string("output", "") -- output buffer
|
||||
meta:set_string("input", "") -- input buffer
|
||||
local name = meta:get_string("name") -- get computer name
|
||||
if not name then name = "" end -- if name nil, set to blank
|
||||
meta:set_string("formspec", digicompute.formspec_name(name)) -- computer name formspec
|
||||
end,
|
||||
on_rightclick = function(pos)
|
||||
-- if clear_on_close is true, clear
|
||||
if digicompute.clear_on_close == true then
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
meta:set_string("formspec", digicompute.formspec("", "")) -- clear formspec
|
||||
node_box = def.node_box,
|
||||
on_destruct = function(pos)
|
||||
if minetest.get_meta(pos):get_string("name") then
|
||||
digicompute.c:deinit(pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
-- on
|
||||
minetest.register_node("digicompute:"..itemstring.."_on", {
|
||||
digicompute = {
|
||||
state = "on",
|
||||
base = itemstring,
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
description = def.defription,
|
||||
tiles = def.on_tiles,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky = 2, not_in_creative_inventory = 1},
|
||||
drop = "digicompute:"..itemstring,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
node_box = def.node_box,
|
||||
on_rightclick = function(pos, node, player)
|
||||
digicompute.c:open(pos, player)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
if meta:get_string("name") then digicompute.fs.deinit(pos) end
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender) -- process formdata
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
|
||||
-- if name, set
|
||||
if fields.name then
|
||||
meta:set_string("name", fields.name) -- set name
|
||||
meta:set_string("setup", "true") -- set computer to configured
|
||||
digicompute.fs.init(pos, fields.name) -- initialize filesystem
|
||||
-- try to run when_on
|
||||
if not digicompute.fs.run_file(pos, "os/start.lua", fields, "start.lua") then
|
||||
meta:set_string("formspec", digicompute.formspec("", "")) -- set formspec
|
||||
end
|
||||
digicompute.refresh(pos) -- refresh
|
||||
end
|
||||
|
||||
local name = meta:get_string("name") -- get name
|
||||
local c = loadfile(path.."/"..meta:get_string("owner").."/"..name.."/os/conf.lua")
|
||||
local e, msg = pcall(c)
|
||||
-- if submitted, process basic commands, pass on to os
|
||||
if fields.input then
|
||||
if fields.input == clear then meta:set_string("formspec", digicompute.formspec("",""))
|
||||
elseif fields.input == off then digicompute.off(pos, termstring) -- set off
|
||||
elseif fields.input == reboot then digicompute.reboot(pos, termstring) -- reboot
|
||||
else -- else, turn over to os
|
||||
digicompute.fs.run_file(pos, "os/main.lua", fields, "main.lua") -- run main.lua
|
||||
end
|
||||
if minetest.get_meta(pos):get_string("name") then
|
||||
digicompute.c:deinit(pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- digicompute/nodes.lua
|
||||
-- digicompute/computers.lua
|
||||
|
||||
digicompute.register_computer("default", {
|
||||
description = "digicomputer",
|
@ -1,3 +1 @@
|
||||
default
|
||||
datalib
|
||||
digilines?
|
||||
|
14
doc/README.md
Normal file
14
doc/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Documentation
|
||||
The digicompute API is divided among several different files. Unless otherwise mentioned, the `.md` documentation file is labeled the same as the Lua file containin the code. For example, documentation of all the API functions introduced by `builtin.lua` can be found in `doc/builtin.md`. Below, an overview of each documentation can be found.
|
||||
|
||||
## `builtin.md`
|
||||
This documents the API introduced by `builtin.lua`. Mostly containing file interaction APIs, __builtin__ contains functions that don't fit elsewhere.
|
||||
|
||||
## `env.md`
|
||||
This covers the simplistic API created in `env.lua` for running code under an environment.
|
||||
|
||||
## `c_api.md`
|
||||
This documents the entire API used by computers. This API is only for use by modders who want to register or customize a computer.
|
||||
|
||||
## `c_os.md`
|
||||
This documents the API accessible under a secure environment to the computer OS. Through this API, the player can interact with the computer itself to make their own programs.
|
64
doc/builtin.md
Normal file
64
doc/builtin.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Builtin
|
||||
Builtin contains many independent functions that don't seem to fit elsewhere. Currently, it is mainly an API for easy interaction with the filesystem. All functions provided by builtin are packed under `digicompute.builtin.*`, but have been shortened in this documentation.
|
||||
|
||||
## `exists`
|
||||
__Usage:__ `digicompute.builtin.exists(<string: path>)`
|
||||
|
||||
* `true`: file exists
|
||||
* `nil`: file does not exist
|
||||
|
||||
Checks to see if a file exists by opening it with `io.open()` and returning `true` if `io.open()` does not return a `nil` value.
|
||||
|
||||
## `create`
|
||||
__Usage:__ `digicompute.builtin.create(<string: path>)`
|
||||
|
||||
* `true`: file successfully created
|
||||
|
||||
Creates a file with `io.open()` and returns `true`. If you want to write to the file, use `write` directly, as it will automatically create the file if it doesn't already exist.
|
||||
|
||||
## `write`
|
||||
__Usage:__ `digicompute.builtin.write(<string: path>, <any: data>, <string: mode>`
|
||||
|
||||
* `true`: successfully wrote to file
|
||||
|
||||
Writes any data to the file specified by `path` and returns `true` if successful. If the file does not exist, it will be created and written to. You can specify if you would like to overwrite or append to a file using the optional `mode` parameter (`w`: overwrite/create, `a`: append). Do directly write a table, but rather serialize it first with `minetest.serialize`. Doing so will cause a crash.
|
||||
|
||||
## `read`
|
||||
__Usage:__ `digicompute.builtin.read(<string: path>)`
|
||||
|
||||
* `not-nil`: data read from file
|
||||
* `nil`: file does not exist
|
||||
|
||||
Attempts to read entire file with `io.open():read()`. If `nil` is returned, the file does not exist, otherwise, the file contents will be returned. If your data was serialized before being written, be sure to run `minetest.deserialize` after reading the file.
|
||||
|
||||
## `copy`
|
||||
__Usage:__ `digicompute.builtin.copy(<string: original path>, <string: new path>`
|
||||
|
||||
* `true`: successfully copied
|
||||
* `nil`: original file does not exist
|
||||
|
||||
Reads from one path then creates and writes its contents to the new path. If the function returns `nil`, the file doesn't exist or some other error has occurred.
|
||||
|
||||
## `mkdir`
|
||||
__Usage:__ `digicompute.builtin.mkdir(<string: path>)`
|
||||
|
||||
* `true`: successfully created directory
|
||||
* `nil`: directory already exists
|
||||
|
||||
Attempts to create directory with `minetest.mkdir()` if available, resorting to `os.execute("mkdir")` if unavailable. Will return `true` if successful, and `nil` if the directory already exists or another error occurs.
|
||||
|
||||
## `rmdir`
|
||||
__Usage:__ `digicompute.builtin.rmdir(<string: path>)`
|
||||
|
||||
* `true`: successfully removed directory
|
||||
* `nil`: directory does not exist
|
||||
|
||||
Recursively removes a directory if it exists. __Note:__ this is destructive and will remove all of the sub-directories and files inside of a directory.
|
||||
|
||||
## `cpdir`
|
||||
__Usage:__ `digicompute.builtin.cpdir(<string: original path>, <string: new path>`
|
||||
|
||||
* `true`: successfully copied directory
|
||||
* `nil`: original directory does not exist
|
||||
|
||||
Recursively copies a directory and all it's sub-directories and files. __Note:__ depending on the size of the original directory, this may take some time.
|
205
doc/c_api.md
Normal file
205
doc/c_api.md
Normal file
@ -0,0 +1,205 @@
|
||||
# Computer API
|
||||
This API is used for registering new computers. The API can also be used for modders to make advanced interactions with pre-existing computers or customize their own.
|
||||
|
||||
## Computer Registration
|
||||
It's actually quite easy to register a computer, the end result looking similar to the definition of a normal node.
|
||||
|
||||
Register a computer with `digicompute.register_computer`.
|
||||
```lua
|
||||
digicompute.register_computer("<computer_string>", {
|
||||
description = "<description>",
|
||||
off_tiles = {},
|
||||
bios_tiles = {},
|
||||
on_tiles = {},
|
||||
node_box = {},
|
||||
})
|
||||
```
|
||||
|
||||
The definition is formed just like that of a normal node definition, except digicompute uses it to do a lot of groundwork rather than requiring you to do it manually. **Note:** do not put a modname in the computer string, `digicompute:` is automatically inserted.
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
digicompute.register_computer("default", {
|
||||
description = "digicomputer",
|
||||
off_tiles = {
|
||||
"top.png",
|
||||
"bottom.png",
|
||||
"right.png",
|
||||
"left.png",
|
||||
"back_off.png",
|
||||
"front_off.png",
|
||||
},
|
||||
bios_tiles = {
|
||||
"top.png",
|
||||
"bottom.png",
|
||||
"right.png",
|
||||
"left.png",
|
||||
"back_off.png",
|
||||
"front_off.png^bios.png",
|
||||
},
|
||||
on_tiles = {
|
||||
"top.png",
|
||||
"bottom.png",
|
||||
"right.png",
|
||||
"left.png",
|
||||
"back.png",
|
||||
"front.png"
|
||||
},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.125, 0.5, 0.5, 0.5},
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Above is example code from the default computer.
|
||||
|
||||
## Advanced API
|
||||
This API is more of a documentation of pre-existing API functions for developers new to this mod who would like to get started. The Advanced API documentation is sectioned out as it is in the code.
|
||||
|
||||
### ID Management
|
||||
This section manages loading, saving, and assigning new IDs to computers.
|
||||
|
||||
#### `load_computers()`
|
||||
**Usage:** `digicompute.load_computers()`
|
||||
|
||||
Loads the IDs of all computers for later use. This should only be called after the variable `computer` (type: `table`) is defined. **Note:** the Computer API automatically loads the computers IDs when the server starts.
|
||||
|
||||
#### `save_computers()`
|
||||
**Usage:** `digicompute.save_computers()`
|
||||
|
||||
Saves computer IDs as stored in the `computer` table. Be sure that this table exists before attempting to save. **Note:** the Computer API automatically saves the computers IDs before the server shuts down.
|
||||
|
||||
#### `c:new_id(pos)`
|
||||
**Usage:** `digicompute.c:new_id(<computer position (table)>)`
|
||||
|
||||
Generate a new computer ID, store it in the `computers` table, and save it in the node meta. Make sure this table exists before attempting to generate a new ID.
|
||||
|
||||
### Formspecs
|
||||
This section uses tables to store information about the formspec(s) and their tabs. It also introduces functions to show and handle received fields from formspecs.
|
||||
|
||||
#### `c:handle_tabs(pos, player, fields)`
|
||||
**Usage:** `digicompute.c:handle_tabs(<computer position (table)>, <player (userdata value)>, <form fields (table)>`
|
||||
|
||||
Handles tab switching. Should be called in the handle function of any tab for the main form. Valid tabs should be added (in proper order) to the tabs table (defined above function). **TODO:** improve API to support tab handling for other forms.
|
||||
|
||||
#### Forms Table
|
||||
**Name:** `digicompute.c.forms`
|
||||
|
||||
This is a slightly more complex topic, as this table handles all of the forms (and tabs) used by digicomputers. Each form/tab has it's own entry, defining a table of informatin about it.
|
||||
|
||||
**Basic Parameters:**
|
||||
```lua
|
||||
digicompute.c.forms = {
|
||||
newformname = {
|
||||
cache_formname = true/false,
|
||||
get = function(pos, player) ... end,
|
||||
handle = function(pos, player, fields) ... end,
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The `cache_formname` field is used in `digicompute.c:open` to choose whether or not to cache the formname in meta. If the formname is cached in meta, it will automatically be opened the next time the computer is right-clicked. Unless this is `false`, the formname will be cached.
|
||||
|
||||
`get` is a required item which is used by `digicompute.c:open` to obtain the actual formspec information. All that really matters is that you return a valid formspec string at the end of the function.
|
||||
|
||||
`handle` is called `on_receive_fields` to handle player input. It is a required item, but there are no direct requirements past that.
|
||||
|
||||
**Example (naming form):**
|
||||
```lua
|
||||
naming = {
|
||||
cache_formname = false,
|
||||
get = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
return
|
||||
"size[6,1.7]"..
|
||||
default.gui_bg_img..
|
||||
"field[.25,0.50;6,1;name;Computer Name:;"..minetest.formspec_escape(meta:get_string("name")).."]"..
|
||||
"button[4.95,1;1,1;submit_name;Set]"
|
||||
end,
|
||||
handle = function(pos, player, fields)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = player:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
if owner == name then
|
||||
if fields.name or fields.key_enter_field == "name" and fields.name ~= "" then
|
||||
meta:set_string("name", fields.name)
|
||||
meta:set_string("setup", "true")
|
||||
meta:set_string("path", main_path..meta:get_string("owner").."/"..meta:get_string("id").."/")
|
||||
digicompute.c:init(pos)
|
||||
digicompute.c:open(pos, player)
|
||||
else
|
||||
minetest.chat_send_player(name, "Name cannot be empty.")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Only the owner can set this computer. ("..owner..")")
|
||||
end
|
||||
end,
|
||||
},
|
||||
```
|
||||
|
||||
#### `c:open(pos, player, formname)`
|
||||
**Usage:** `digicompute.c:open(<computer position (table)>, <player (userdata value)>, <form name (string)>)`
|
||||
|
||||
Shows a form defined in the forms table. If the formname is not provided, it will be set to the formname cached in meta (if any), and default to `main`. Fields are automatically sent to the `handle` function defined in the forms table. **Note:** `player` should not be the a plaintext string containing the player name, but a userdata value.
|
||||
|
||||
### Helper Functions
|
||||
This section defines several helper functions used in the formspecs, environment, and node definition.
|
||||
|
||||
#### `c:infotext(pos)`
|
||||
**Usage:** `digicompute.c:infotext(<computer position (table)>)`
|
||||
|
||||
Updates the infotext of the computer. This is called after the computer is named or when its state changes (off/bios/on).
|
||||
|
||||
#### `c:init(pos)`
|
||||
**Usage:** `digicompute.c:init(<computer position (table)>)`
|
||||
|
||||
Initializes the computers filesystem, runs `main.lua`, and updates the infotext. **Note:** path must already be defined in meta, otherwise the initialization process will not complete (this is defined in the handling function of the naming form).
|
||||
|
||||
#### `c:deinit(pos, true/false)`
|
||||
**Usage:** `digicompute.c:deinit(<computer position (table)>, <clear computer ID entry (boolean)>)`
|
||||
|
||||
Deinitializes a computers filesystem. The entry in the computers table is also cleared unless the final parameter is `false` (used when a computer reset is requested as the ID should not be cleared).
|
||||
|
||||
#### `c:reinit(pos)`
|
||||
**Usage:** `digicompute.c:reinit(<computer position (table)>)`
|
||||
|
||||
Reinitializes the filesystem of a computer by calling `c:deinit` followed by `c:init`. **Note:** this is destructive and will wipe any files created or changed by the player.
|
||||
|
||||
#### `c:on(pos, player)`
|
||||
**Usage:** `digicompute.c:on(<computer position (table)>, <player (userdata value)>`
|
||||
|
||||
Turns a computer on (will not execute if computer is not off). `start.lua` is automatically run, hence the player userdata is required.
|
||||
|
||||
#### `c:off(pos, player)`
|
||||
**Usage:** `digicompute.c:off(<computer position (table)>, <player (userdata value)>`
|
||||
|
||||
Turns a computer off. The formspec is automatically closed using `minetest.close_formspec` (requires Minetest 0.4.15 or later), hence the player userdata is required.
|
||||
|
||||
#### `c:reboot(pos, player)`
|
||||
**Usage:** `digicompute.c:reboot(<computer position (table)>, <player (userdata value)>`
|
||||
|
||||
Reboots a computer by calling `c:off` followed by `c:on`.
|
||||
|
||||
### Environment
|
||||
This section introduces functions to initialize the environment per-computer and execute a string or file under the environment.
|
||||
|
||||
#### `c:make_env(pos, player)`
|
||||
**Usage:** `digicompute.c:make_env(<computer position (table)>, <player (userdata value)>`
|
||||
|
||||
Returns a table of functions allowed under the environment. The table is made up of a wide array of functions for interacting with the computer and its file system. These are joined with the table returned by `digicompute.env()`, explaned in `env.md`. The player userdata parameter is required for later callbacks to functions such as `c:open`.
|
||||
|
||||
#### `c:run_code(pos, player, code)`
|
||||
**Usage:** `digicompute.c:run_code(<computer position (table)>, <player (userdata value)>, <code (string)>)`
|
||||
|
||||
Generates an environment table using `c:make_env` and runs the code (provided as third parameter) with `digicompute.run_code` (see `env.md`).
|
||||
|
||||
#### `c:run_file(pos, player, path)`
|
||||
**Usage:** `digicompute.c:run_file(<computer position (table)>, <player (userdata value)>, <path (string)>)`
|
||||
|
||||
Generates an environment table using `c:make_env` and runs the code found in the file specified by `path` with `digicompute.run_file` (see `env.md`). **Note:** the path is relative to the computer, meaning that `main.lua` could be run with `digicompute.c:run_file(pos, player, "os/main.lua")`.
|
128
doc/c_os.md
Normal file
128
doc/c_os.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Computer OS API
|
||||
This API can be used by players to interact with the computer under a safe and secure environment. The documentation is divided into two sections as is the code, main (for general functions), and filesystem (for filesystem access).
|
||||
|
||||
## Main
|
||||
This contains a set of functions mainly for the purpose of interacting with the computer's displays.
|
||||
|
||||
#### `print(string, false)`
|
||||
**Usage:** `print(<contents>, <newline (true/false)>`
|
||||
|
||||
Prints to the output buffer. If contents is not a string, it will be converted to a string with `dump`. The second parameter, if false, prevents print from inserting a newline before printing the provided contents.
|
||||
|
||||
#### `set_help(value)`
|
||||
**Usage:** `set_help(<value (string)>)`
|
||||
|
||||
Sets the text to be shown when hovering over the help button. A `nil` value will revert the text to default.
|
||||
|
||||
#### `get_attr(key)`
|
||||
**Usage:** `get_attr(<attribute name (string)>)`
|
||||
|
||||
Gets a piece of global information from the node meta (storage). Several common attributes are below. **Note:** none of these attributes can be directly set, with the purpose of being read-only. However, there are methods to set several.
|
||||
|
||||
* `owner`: username of the player who owns the computer.
|
||||
* `input`: input field.
|
||||
* `output`: output buffer.
|
||||
* `name`: computer name.
|
||||
|
||||
#### `get_output()`
|
||||
**Usage:** `get_output()`
|
||||
|
||||
Returns the value of the output buffer. Shorthand for `get_attr("output")`.
|
||||
|
||||
#### `set_output(value)`
|
||||
**Usage:** `set_output(<value (string)>)`
|
||||
|
||||
Set the output buffer to any string. This is the write method for the output attribute.
|
||||
|
||||
#### `get_input()`
|
||||
**Usage:** `get_input()`
|
||||
|
||||
Returns the value of the input field. Shorthand for `get_attr("input")`.
|
||||
|
||||
#### `set_input(value)`
|
||||
**Usage:** `set_input(<value (string)>)`
|
||||
|
||||
Set the input field to any string. This is the write method for the input attribute.
|
||||
|
||||
#### `get_os(key)`
|
||||
**Usage:** `get_os(<data name (string)>)`
|
||||
|
||||
Gets a piece of information from the OS table. See next function for further information on this table.
|
||||
|
||||
#### `set_os(key, value)`
|
||||
**Usage:** `set_os(<data name (string)>, <value>`
|
||||
|
||||
Sets a piece of information stored in the OS table. This table stores basic values containing information global to the operating system. However, it is quite limitted, only being capable of storing a few pieces of information as listed below.
|
||||
|
||||
* `clear`: command to clear the output and input.
|
||||
* `off`: command to turn the computer off.
|
||||
* `reboot`: command to reboot the computer.
|
||||
* `prefix`: prefix printed at the beginning of a new line.
|
||||
|
||||
#### `get_userdata(key)`
|
||||
**Usage:** `get_userdata(<data name (string)>)`
|
||||
|
||||
Gets a piece of information from the userdata table. This table is like RAM, as information will be reset when the computer is turned off.
|
||||
|
||||
#### `set_userdata(key, value)`
|
||||
**Usage:** `set_userdata(<data name (string)>, <value>`
|
||||
|
||||
Stores any piece of information in the non-persistant userdata table. (Table is cleared when computer is turned off, therefore non-persistant.)
|
||||
|
||||
#### `refresh()`
|
||||
**Usage:** `refresh()`
|
||||
|
||||
Refresh the computer display, typically after making changes to a buffer, field, or other element.
|
||||
|
||||
#### `run(code)`
|
||||
**Usage:** `run(<code (string)>)`
|
||||
|
||||
Run code under the environment (e.g. run data in the input field whenever it is submitted).
|
||||
|
||||
## Filesystem
|
||||
This API section introduces function to interact with the computer's physical filesystem.
|
||||
|
||||
#### `exists(path)`
|
||||
__Usage:__ `fs.exists(<path (string)>)`
|
||||
|
||||
Checks to see if a file exists by opening it with `io.open()` and returns `true` if exists, and `nil` if it does not.
|
||||
|
||||
#### `create(path)`
|
||||
__Usage:__ `fs.create(<path (string)>)`
|
||||
|
||||
Creates a file with and returns `true` unless unsuccessful. If you want to write to the file, use `write` directly, as it will automatically create the file if it doesn't already exist.
|
||||
|
||||
#### `remove(path)`
|
||||
__Usage:__ `fs.remove(<path (string)>`
|
||||
|
||||
Removes a file and returns `true` if successful. If the return value is `nil`, the file either does not exist or is a directory. Directories must be removed with `rmdir`.
|
||||
|
||||
#### `write(path, data, mode)`
|
||||
__Usage:__ `fs.write(<path (string)>, <data>, <mode (string)>`
|
||||
|
||||
Writes any data to the file specified by `path` and returns `true` if successful. If the file does not exist, it will be created and written to. You can specify if you would like to overwrite or append to a file using the optional `mode` parameter (`w`: overwrite/create, `a`: append).
|
||||
|
||||
#### `read(path)`
|
||||
__Usage:__ `fs.read(<path (string)>)`
|
||||
|
||||
Attempts to read entire file. If `nil` is returned, the file does not exist, otherwise, the file contents will be returned.
|
||||
|
||||
#### `copy(original_path, new_path)`
|
||||
__Usage:__ `fs.copy(<original path (string)>, <new path> (string)`
|
||||
|
||||
Reads from one path then creates and writes its contents to the new path. If the function returns `nil`, the file doesn't exist or some other error has occurred. Otherwise, a return value of `true` indicates a success.
|
||||
|
||||
#### `mkdir(path)`
|
||||
__Usage:__ `fs.mkdir(<path (string)>)`
|
||||
|
||||
Creates a directory. Will return `true` if successful, and `nil` if the directory already exists or another error occurs.
|
||||
|
||||
#### `rmdir(path)`
|
||||
__Usage:__ `fs.rmdir(<path (string)>)`
|
||||
|
||||
Recursively removes a directory if it exists. Returns `true` if successful. __Note:__ this is destructive and will remove all of the sub-directories and files inside of a directory.
|
||||
|
||||
#### `cpdir(original_path, new_path)`
|
||||
__Usage:__ `fs.cpdir(<original path (string)>, <new path (string)>`
|
||||
|
||||
Recursively copies a directory and all it's sub-directories and files. Returns `true` if successful. __Note:__ depending on the size of the original directory, this may take some time.
|
20
doc/env.md
Normal file
20
doc/env.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Environment API
|
||||
This API provides a simple set of functions to easily run code under a secure environment.
|
||||
|
||||
## `env()`
|
||||
**Usage:** `digicompute.env()`
|
||||
|
||||
Returns a table of safe functions for use when executing code under a secure environment. It is not recommended that you attempt to use this table to manually execute code, rather use `digicompute.run_code`.
|
||||
|
||||
## `run_code(code, env)`
|
||||
**Usage:** `digicompute.run_code(<code (string)>, <environment (table)>)`
|
||||
|
||||
* `false`, `msg`: code failed to execute.
|
||||
* `true`, `msg`: code executed successfully.
|
||||
|
||||
Attempts to run provided code under a safe environment (environment table can be generated with `digicompute.env()`). The function returns two variables, the first being a boolean telling whether the operation was successful, and the second an error message/return value from the executed code.
|
||||
|
||||
## `run_file(path, env)`
|
||||
**Usage:** `digicompute.run_file(<path (string)>, <environment (table)>)`
|
||||
|
||||
Loads the contents of a file using `builtin` and provides the resulting code and environment table (provided as second parameter) to `run_code`.
|
172
env.lua
172
env.lua
@ -1,16 +1,7 @@
|
||||
-- digicompute/env.lua --
|
||||
-- ENVIRONMENT --
|
||||
-----------------
|
||||
|
||||
-- [function] create environment
|
||||
function digicompute.create_env(pos, fields)
|
||||
local meta = minetest.get_meta(pos) -- get meta
|
||||
-- CUSTOM SAFE FUNCTIONS --
|
||||
|
||||
local function safe_print(param)
|
||||
print(dump(param))
|
||||
end
|
||||
|
||||
function digicompute.env()
|
||||
local function safe_date()
|
||||
return(os.date("*t",os.time()))
|
||||
end
|
||||
@ -37,133 +28,7 @@ function digicompute.create_env(pos, fields)
|
||||
return string.find(...)
|
||||
end
|
||||
|
||||
-- [function] get attr (from meta)
|
||||
local function get_attr(key)
|
||||
return meta:get_string(key) or nil
|
||||
end
|
||||
-- [function] get userdata
|
||||
local function get_userdata(key)
|
||||
local t = minetest.deserialize(meta:get_string("userspace"))
|
||||
return t[key] or nil
|
||||
end
|
||||
-- [function] set userdata
|
||||
local function set_userdata(key, value)
|
||||
local t = minetest.deserialize(meta:get_string("userspace"))
|
||||
t[key] = value
|
||||
return meta:set_string("userspace", minetest.serialize(t))
|
||||
end
|
||||
-- [function] get input
|
||||
local function get_input()
|
||||
return meta:get_string("input") or nil
|
||||
end
|
||||
-- [function] set input
|
||||
local function set_input(value)
|
||||
return meta:set_string("input", value) or nil
|
||||
end
|
||||
-- [function] get output
|
||||
local function get_output()
|
||||
return meta:get_string("output") or nil
|
||||
end
|
||||
-- [function] set output
|
||||
local function set_output(value)
|
||||
return meta:set_string("output", value) or nil
|
||||
end
|
||||
-- [function] get field
|
||||
local function get_field(key)
|
||||
return fields[key] or nil
|
||||
end
|
||||
-- [function] refresh
|
||||
local function refresh()
|
||||
meta:set_string("formspec", digicompute.formspec(meta:get_string("input"), meta:get_string("output")))
|
||||
return true
|
||||
end
|
||||
|
||||
-- filesystem API
|
||||
|
||||
-- [function] get file (read)
|
||||
local function get_file(path)
|
||||
local res = digicompute.fs.get_file(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] get directory contents
|
||||
local function get_dir(path)
|
||||
local res = digicompute.fs.get_dir(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] exists
|
||||
local function exists(path)
|
||||
local res = digicompute.fs.exists(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] mkdir
|
||||
local function mkdir(path)
|
||||
local res = digicompute.fs.exists(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] rmdir
|
||||
local function rmdir(path)
|
||||
local res = digicompute.fs.rmdir(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] mkdir
|
||||
local function mkdir(path)
|
||||
local res = digicompute.fs.exists(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] create file
|
||||
local function create(path)
|
||||
local res = digicompute.fs.create(pos, path)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] write
|
||||
local function write(path, data)
|
||||
local res = digicompute.fs.write(pos, path, data)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] append
|
||||
local function append(path, data)
|
||||
local res = digicompute.fs.append(pos, path, data)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- [function] copy
|
||||
local function copy(path, npath)
|
||||
local res = digicompute.fs.copy(pos, path, npath)
|
||||
if res then return res end
|
||||
end
|
||||
|
||||
-- ENVIRONMENT TABLE --
|
||||
|
||||
local env = {
|
||||
run = digicompute.run,
|
||||
get_attr = get_attr,
|
||||
get_userdata = get_userdata,
|
||||
set_userdata = set_userdata,
|
||||
get_input = get_input,
|
||||
set_input = set_input,
|
||||
get_output = get_output,
|
||||
set_output = set_output,
|
||||
get_field = get_field,
|
||||
refresh = refresh,
|
||||
fs = {
|
||||
read = get_file,
|
||||
list = get_dir,
|
||||
check = exists,
|
||||
mkdir = mkdir,
|
||||
rmdir = rmdir,
|
||||
touch = create,
|
||||
write = write,
|
||||
copy = copy,
|
||||
cp = copy,
|
||||
},
|
||||
string = {
|
||||
byte = string.byte,
|
||||
char = string.char,
|
||||
@ -221,5 +86,38 @@ function digicompute.create_env(pos, fields)
|
||||
datetable = safe_date,
|
||||
},
|
||||
}
|
||||
return env -- return table
|
||||
return env
|
||||
end
|
||||
|
||||
-- [function] run code
|
||||
function digicompute.run_code(code, env)
|
||||
if code:byte(1) == 27 then
|
||||
return nil, "Binary code prohibited."
|
||||
end
|
||||
local f, msg = loadstring(code)
|
||||
if not f then return false, msg end
|
||||
setfenv(f, env)
|
||||
|
||||
-- Turn off JIT optimization for user code so that count
|
||||
-- events are generated when adding debug hooks
|
||||
if rawget(_G, "jit") then
|
||||
jit.off(f, true)
|
||||
end
|
||||
|
||||
-- Use instruction counter to stop execution
|
||||
-- after 10000 events
|
||||
debug.sethook(function()
|
||||
return false, "Code timed out!"
|
||||
end, "", 10000)
|
||||
local ok, ret = pcall(f)
|
||||
debug.sethook() -- Clear hook
|
||||
if not ok then return false, ret end
|
||||
return true, ret
|
||||
end
|
||||
|
||||
-- [function] run file
|
||||
function digicompute.run_file(path, env)
|
||||
local code = digicompute.builtin.read(path)
|
||||
local ok, res = digicompute.run_code(code, env)
|
||||
return ok, res
|
||||
end
|
||||
|
52
init.lua
52
init.lua
@ -1,45 +1,31 @@
|
||||
-- digicompute/init.lua
|
||||
digicompute = {}
|
||||
-- variables
|
||||
|
||||
digicompute.VERSION = 0.1
|
||||
digicompute.RELEASE_TYPE = "beta"
|
||||
|
||||
digicompute.path = minetest.get_worldpath().."/digicompute/" -- digicompute directory
|
||||
digicompute.modpath = minetest.get_modpath("digicompute") -- modpath
|
||||
local modpath = digicompute.modpath -- modpath pointer
|
||||
|
||||
-- logger
|
||||
-- Load builtin
|
||||
dofile(modpath.."/builtin.lua")
|
||||
|
||||
-- Logger
|
||||
function digicompute.log(content, log_type)
|
||||
assert(content, "digicompute.log content nil")
|
||||
if log_type == nil then log_type = "action" end
|
||||
minetest.log(log_type, "[digicompute] "..content)
|
||||
end
|
||||
|
||||
-- create mod world dir
|
||||
datalib.mkdir(datalib.worldpath.."/digicompute")
|
||||
digicompute.path = datalib.worldpath.."/digicompute"
|
||||
-- Create mod directory inside world directory
|
||||
digicompute.builtin.mkdir(digicompute.path)
|
||||
|
||||
-- FORMSPECS
|
||||
-- normal
|
||||
function digicompute.formspec(input, output)
|
||||
if not output then local output = "" end
|
||||
if not input then local input = "" end
|
||||
-- formspec
|
||||
local formspec =
|
||||
"size[10,11]"..
|
||||
default.gui_bg_img..
|
||||
"textarea[.25,.25;10,11.5;output;Output:;"..output.."]"..
|
||||
"field[.25,10.75;10,1;input;;"..input.."]"..
|
||||
"field_close_on_enter[input;false]"
|
||||
return formspec -- return formspec text
|
||||
end
|
||||
-- set name
|
||||
function digicompute.formspec_name(computer)
|
||||
if not computer then local computer = "" end -- use blank channel is none specified
|
||||
local formspec =
|
||||
"size[6,1.7]"..
|
||||
default.gui_bg_img..
|
||||
"field[.25,0.50;6,1;name;Computer Name:;"..computer.."]"..
|
||||
"button[4.95,1;1,1;submit_name;Set]"
|
||||
return formspec
|
||||
end
|
||||
-- /FORMSPECS
|
||||
-- Load environment utilities
|
||||
dofile(modpath.."/env.lua")
|
||||
|
||||
-- load resources
|
||||
dofile(modpath.."/api.lua") -- load api
|
||||
dofile(modpath.."/nodes.lua") -- load nodes
|
||||
-- Load API-like resources
|
||||
dofile(modpath.."/c_api.lua") -- Computer API
|
||||
|
||||
-- Load registration code
|
||||
dofile(modpath.."/computers.lua") -- Computers
|
||||
|
Loading…
x
Reference in New Issue
Block a user