diff --git a/computers/api.lua b/computers/api.lua index 8364b92..eabd3d6 100644 --- a/computers/api.lua +++ b/computers/api.lua @@ -25,7 +25,7 @@ computers.sandbox.default_env = {} os.clock os.difftime os.time - string.byte string.char string.format + string.byte string.char string.len string.lower string.reverse string.sub string.upper @@ -130,4 +130,13 @@ function computers.api.chat_send_player(player, msg) local name = player if type(name) == "userdata" then name = player:get_player_name() end minetest.chat_send_player(name, msg) +end + +--basically for dev usage only +function computers.api.benchmark(func, ...) + local start_time = minetest.get_us_time() + for i=1, 1000 do + func(...) + end + minetest.chat_send_all(minetest.get_us_time()-start_time) end \ No newline at end of file diff --git a/computers/commands/net/conf.json b/computers/commands/net/conf.json new file mode 100644 index 0000000..07055ec --- /dev/null +++ b/computers/commands/net/conf.json @@ -0,0 +1,7 @@ +{ + "name": "net", + "license": "MIT", + "version": 0.1, + "engine": 0.43, + "help": "configuration related to networking" +} \ No newline at end of file diff --git a/computers/commands/net/init.lua b/computers/commands/net/init.lua new file mode 100644 index 0000000..c7f6fdc --- /dev/null +++ b/computers/commands/net/init.lua @@ -0,0 +1,64 @@ +function net(pos, input, data) + input = input or "" + local lookup = { + list = function(param) + if param == "-ids" then + return table.concat(computers.networks.get_networks_in_area(pos), " ") + elseif not param or param == "" then + return table.concat(computers.networks.get_net_names_in_area(pos), " ") + else + return "invalid sub command" + end + end, + join = function(param) + local id = computers.networks.get_id_by_net_name(data.player, param) + if not id then + return "invalid network name" + elseif minetest.get_meta(pos):get("net_id") then + return "already joined to a network" + else + local _, networks = computers.networks.get_net_names_in_area(pos) + if not networks[param] then return "network not in range" end + minetest.get_meta(pos):set_string("net_id", id) + local status = computers.networks.add_device(id, pos) + if status then return "joined network " .. param else return "network join failed" end + end + end, + leave = function(param) + local id = minetest.get_meta(pos):get("net_id") + if id then + computers.networks.remove_device(id, pos) + return "removed device from network " .. computers.networks.get_net_name(id) + else + return "not currently connected to any network" + end + end, + status = function(param) + local id = minetest.get_meta(pos):get("net_id") + if id then + return "connected to network " .. computers.networks.get_net_name(id) + else + return "not connected to any network" + end + end, + dns = function(param) + if minetest.get_item_group(minetest.get_node(pos).name, "computers_server") == 0 then + return "command only available to servers" + end + if not param or param == "" then return "invalid or missing input" end + if param:match("%W") then return "only alphanumerics allowed" end + local id = minetest.get_meta(pos):get("net_id") + if id then + computers.networks.set_dns(id, pos, param) + return "created dns record" + else + return "not connected to network" + end + end + } + + if not lookup[input:split(" ")[1]] then return "invalid sub command" end + return lookup[input:split(" ")[1]](input:split(" ", 1)[2]) +end + +return net \ No newline at end of file diff --git a/computers/demo.lua b/computers/demo.lua index 364bff1..00ca7b3 100644 --- a/computers/demo.lua +++ b/computers/demo.lua @@ -11,4 +11,211 @@ old_node = minetest.registered_nodes["computers:shefriendSOO_off"] old_node.groups.not_in_creative_inventory = nil minetest.override_item("computers:shefriendSOO_off", { groups = old_node.groups +}) + +old_node = minetest.registered_nodes["computers:server_on"] +old_node.groups.computers_server = 1 +minetest.override_item("computers:server_on", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + computers.load_gui(pos, node, clicker) + end, + groups = old_node.groups +}) + +minetest.override_item("computers:router", { + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local formspec = { + formspec_version = 4, + { + type = "size", + w = 10, + h = 5, + }, + { + type = "no_prepend" + }, + { + type = "bgcolor", + bgcolor = "black", + fullscreen = "neither" + }, + { + type = "background9", + x = 0, + y = 0, + w = 10, + h = 5, + texture_name = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70", + auto_clip = false, + middle_x = 4, + middle_y = 4, + }, + { + type = "hypertext", + name = "label", + x = 0, + y = 0, + w = 10, + h = 2, + text = "
Choose Option Below
", + }, + { + type = "button", + x = 0.5, + y = 3.5, + w = 4, + h = 1, + name = "newnetwork_btn", + label = "New Network", + on_event = function(form, player, element, value, fields) + if element.label == "New Network" then + element.label = "Submit" + table.remove(form, computers.formspec.get_index_by_name(form, "existingnetwork_btn")) + form[computers.formspec.get_index_by_name(form, "label")].text = [[ +
Enter New Network Name
+ ]] + table.insert( + form, + { + type = "box", + x = 3, + y = 1.75, + w = 4, + h = 1, + color = "#ffffff" + } + ) + table.insert( + form, + { + type = "field", + x = 3, + y = 1.75, + w = 4, + h = 1, + name = "id_field", + close_on_enter = false, + props = { + border = false, + }, + } + ) + else + if fields.id_field == "" and not computers.formspec.get_index_by_name(form, "warning") then + table.insert( + form, + { + type = "hypertext", + name = "warning", + x = 5.5, + y = 3.5, + w = 4, + h = 1, + text = [[ + +
+ ]] + } + ) + elseif fields.id_field ~= "" then + local status, id = computers.networks.create(player, fields.id_field) + if status then + form = nil + minetest.get_meta(pos):set_string("net_id", id) + computers.networks.add_device(id, pos) + computers.api.chat_send_player(player, "network " .. fields.id_field .. " created") + computers.formspec.close_formspec(player) + elseif not computers.formspec.get_index_by_name(form, "warning") then + table.insert( + form, + { + type = "hypertext", + name = "warning", + x = 5.5, + y = 3.5, + w = 4, + h = 1, + text = [[ + +
+ ]] + } + ) + end + end + end + --minetest.chat_send_all("test") + + if form then return form end + end, + props = { + border = false, + bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70", + bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90", + bgimg_middle = "4,4", + } + }, + { + type = "button", + x = 5.5, + y = 3.5, + w = 4, + h = 1, + name = "existingnetwork_btn", + label = "Existing Network", + on_event = function(form, player, element, value, fields) + if element.label == "Existing Network" then + element.label = "Submit" + table.remove(form, computers.formspec.get_index_by_name(form, "newnetwork_btn")) + form[computers.formspec.get_index_by_name(form, "label")].text = [[ +
Enter Network
+ ]] + table.insert( + form, + { + type = "box", + x = 3, + y = 1.75, + w = 4, + h = 1, + color = "#ffffff" + } + ) + table.insert( + form, + { + type = "field", + x = 3, + y = 1.75, + w = 4, + h = 1, + name = "id_field", + close_on_enter = false, + props = { + border = false, + }, + } + ) + else + minetest.chat_send_all("submitted") + end + + return form + end, + props = { + border = false, + bgimg = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff70", + bgimg_hovered = "kuto_button.png^[combine:16x16^[noalpha^[colorize:#ffffff90", + bgimg_middle = "4,4", + } + }, + } + + if minetest.get_meta(pos):get("net_id") then + local net_name = computers.networks.get_net_name(minetest.get_meta(pos):get("net_id")) + computers.api.chat_send_player(clicker, "router attached to network: " .. net_name) + else + computers.formspec.show_formspec(clicker, "computers:router", formspec) + end + end }) \ No newline at end of file diff --git a/computers/formspec.lua b/computers/formspec.lua index b4326e0..889de56 100644 --- a/computers/formspec.lua +++ b/computers/formspec.lua @@ -75,6 +75,8 @@ function computers.formspec.convert_to_ast(form) return fs end +local forms = {} + function computers.formspec.show_formspec(player, formname, fs) local playername = player local formspec = fs @@ -87,11 +89,21 @@ function computers.formspec.show_formspec(player, formname, fs) formspec = formspec_ast.unparse(computers.formspec.convert_to_ast(fs)) end + forms[formname] = true + minetest.show_formspec(playername, formname, formspec) end +function computers.formspec.close_formspec(player, formname) + local name = player + if type(name) == "userdata" then name = player:get_player_name() end + + minetest.close_formspec(name, formname or "") +end + minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname ~= "computers:gui" then return end + --if formname ~= "computers:gui" then return end + if not forms[formname] then return end local pname = player:get_player_name() if fields.quit then computers.formspec.registered_kast[pname] = nil return end @@ -117,7 +129,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) btn_override or keys[1] ) - --minetest.chat_send_all(keys[1]) + --minetest.chat_send_all(btn_override or keys[1]) --minetest.chat_send_all(fields[keys[1]]) if element and element.on_event then diff --git a/computers/gui.lua b/computers/gui.lua index 69678ab..abf669f 100644 --- a/computers/gui.lua +++ b/computers/gui.lua @@ -243,8 +243,17 @@ function computers.gui.load(pos, node, clicker) border = false, }, on_event = function(form, player, element, value, fields) - --minetest.chat_send_all("url entered") - computers.api.chat_send_player(player, "[computers]: networking not currently supported") + local id = minetest.get_meta(pos):get("net_id") + if id then + local status, data = computers.networks.resolve_url(id, pos, value) + if status and data then + local cindex = futil.get_index_by_name(form, "browser_ctn") + local eindex = futil.get_index_by_name(form[cindex], "browser_content") + form[cindex][eindex].text = data + end + else + computers.api.chat_send_player(player, "[computers]: not attached to a network") + end return form end, diff --git a/computers/init.lua b/computers/init.lua index 4bbbb66..440e45a 100644 --- a/computers/init.lua +++ b/computers/init.lua @@ -8,7 +8,7 @@ minetest.mkdir(computers.devicepath) --make sure it exists minetest.mkdir(computers.networkpath) --make sure it exists computers.os = { - version = 0.42, + version = 0.43, name = "kuto", authors = {"wsor", "luk3yx"}, license = "MIT", @@ -17,6 +17,7 @@ computers.os = { dofile(computers.modpath .. "/old_stuff/init.lua") dofile(computers.modpath .. "/api.lua") +dofile(computers.modpath .. "/networks.lua") dofile(computers.modpath .. "/formspec.lua") dofile(computers.modpath .. "/commands.lua") dofile(computers.modpath .. "/gui.lua") diff --git a/computers/networks.lua b/computers/networks.lua new file mode 100644 index 0000000..15d124b --- /dev/null +++ b/computers/networks.lua @@ -0,0 +1,152 @@ +local networks = {} +local network_lookup = {} +computers.networks = {} + +local function init_networks() + local path = computers.networkpath + local files = minetest.get_dir_list(computers.networkpath, false) + for _, file in pairs(files) do + local id = file:sub(1,-6) + local f = io.open(path .. "/" .. file) + networks[id] = minetest.parse_json(f:read("*all")) + f:close() + + if not network_lookup[networks[id].pname] then network_lookup[networks[id].pname] = {} end + network_lookup[networks[id].pname][networks[id].net_name] = id + end +end + +init_networks() + +local function update_file(id) + local data = networks[id] + local path = computers.networkpath .. "/" .. id .. ".json" + minetest.safe_file_write(path, minetest.write_json(data)) +end + +function computers.networks.create(player, net_name) + local pname = player + if type(player) == "userdata" then pname = player:get_player_name() end + local id = pname .. "_" .. minetest.get_us_time() + + if net_name:match("%W") then return false end --pnly accept alphanumerics + if not network_lookup[pname] then network_lookup[pname] = {} end + if network_lookup[pname][net_name] then return false end + if networks[id] then return false end + + --create storage on disk + local path = computers.networkpath .. "/" .. id .. ".json" + local data = { + pname = pname, + net_name = net_name, + devices = {}, + dns = { + } + } + minetest.safe_file_write(path, minetest.write_json(data)) + + --store data in memory + networks[id] = data + network_lookup[pname][net_name] = id + + return true, id +end + +function computers.networks.verify_network(id) + if networks[id] then return true else return false end +end + +function computers.networks.get_net_name(id) + return networks[id].net_name +end + +function computers.networks.get_network_devices(id) + return table.copy(networks[id].devices) +end + +function computers.networks.get_network_dns(id) + return table.copy(networks[id].dns) +end + +function computers.networks.get_id_by_net_name(player, net_name) + local pname = player + if type(player) == "userdata" then pname = player:get_player_name() end + if not network_lookup[pname] then network_lookup[pname] = {} end + return network_lookup[pname][net_name] +end + +function computers.networks.get_networks_in_area(pos, rad) + rad = rad or 10 + local locations = {} + local keyed_locations = {} + local meta_nodes = minetest.find_nodes_with_meta( + vector.new(pos.x+rad, pos.y+rad, pos.z+rad), + vector.new(pos.x-rad, pos.y-rad, pos.z-rad) + ) + for _, location in pairs(meta_nodes) do + local status = minetest.get_meta(location):get("net_id") + if status and networks[status] then + table.insert(locations, status) + keyed_locations[status] = true + end + end + return locations, keyed_locations +end + +function computers.networks.get_net_names_in_area(pos, rad) + rad = rad or 10 + local locations = {} + local keyed_locations = {} + local meta_nodes = minetest.find_nodes_with_meta( + vector.new(pos.x+rad, pos.y+rad, pos.z+rad), + vector.new(pos.x-rad, pos.y-rad, pos.z-rad) + ) + for _, location in pairs(meta_nodes) do + local status = minetest.get_meta(location):get("net_id") + if status and networks[status] and not keyed_locations[networks[status].net_name] then + table.insert(locations, networks[status].net_name) + keyed_locations[networks[status].net_name] = true + end + end + return locations, keyed_locations +end + +function computers.networks.add_device(id, pos) + if type(pos) ~= "table" or not networks[id] then return false end + networks[id].devices[minetest.pos_to_string(pos)] = minetest.get_node(pos).name + minetest.get_meta(pos):set_string("net_id", id) + update_file(id) + return true +end + +function computers.networks.remove_device(id, pos) + if type(pos) ~= "table" or not networks[id] then return false end + networks[id].devices[minetest.pos_to_string(pos)] = nil + minetest.get_meta(pos):set_string("net_id", "") + update_file(id) + return true +end + +function computers.networks.set_dns(id, pos, url) + if type(pos) ~= "table" or not networks[id] then return false end + if url:match("%W") then return false end + networks[id].dns = networks[id].dns or {} + networks[id].dns[url] = minetest.hash_node_position(pos) .. "/public_pages" + + update_file(id) + return true +end + +function computers.networks.resolve_url(id, pos, url) + if type(pos) ~= "table" or not networks[id] then return false end + if networks[id].dns[url:split("/")[1]] then + local path = computers.devicepath .. "/" .. networks[id].dns[url:split("/")[1]].."/index.page" + local f = io.open(path) + if not f then return false end + local data = f:read("*all") + f:close() + if data then return true, data else return false end + else + return false + end +end \ No newline at end of file