From 0a2c8d6e3701b0805915c0388897382a6bebf0ae Mon Sep 17 00:00:00 2001 From: Gerold55 Date: Tue, 12 Dec 2017 21:32:17 -0500 Subject: [PATCH] Add files via upload --- API.md | 16 +++++-- app_fw.lua | 18 +++++--- demo_apps.lua | 6 +-- init.lua | 1 + items.lua | 14 ++++++ node_fw.lua | 87 ++++++++++++++++++++++++++--------- os.lua | 122 ++++++++++++++++++++++++++++++++++++++++---------- themes.lua | 1 + 8 files changed, 208 insertions(+), 57 deletions(-) create mode 100644 items.lua diff --git a/API.md b/API.md index 56bdc4d..2e47cdd 100644 --- a/API.md +++ b/API.md @@ -8,6 +8,7 @@ - `hwdef.sequence = { "variant_1_name", "variant_2_name", "variant_3_name" }` On punch swaps sequence. the first variant is in creative inventory - `hwdef.custom_launcer` - optional - custom launcher name - `hwdef.custom_theme` - optional - custom initial theme name +- `hwdef.removable_capability` - optional. Table with compatible removable groups. Supported "floppy" or "usb". Default { "floppy", "usb" } - `hwdef.node_defs = { variant_1_name = { hw_state = "resume", "power_on" or "power_off", -- Hardware state @@ -38,10 +39,10 @@ Usable from node functions, from apps or outsite - `mtos:save()` - Store all app-data to nodemeta. Called mostly internally so no explicit call necessary - `mtos:get_app(appname)`- Get the app instance - `mtos:set_app(appname)` - Start/Enable/navigate to appname. If no appname given the launcher is called -- `mtos:receive_fields(fields, sender)` - Should be called from node.on_receive_fields to get the apps interactive - `mtos:get_theme(theme)`- Get theme data current or requested (theme parameter is optional) - `mtos:set_theme(theme)`- Activate theme - +- `mtos:get_removable_data(media_type)` - Access to the item in node inventory (low-level). if media_type is given (item type "usb" or "floppy", or os_format) the method returns only if the type matches +- `mtos:set_removable_data()` - Store changes on low-level removable data ## App Definition `laptop.register_app(internal_shortname, { definitiontable })` - add a new app or view @@ -52,7 +53,7 @@ Usable from node functions, from apps or outsite - `view` - (boolean) The definition is a view. That means the app/view is not visible in launcher - `formspec_func(app, mtos)` - Function, should return the app formspec (mandatory) During definition the "app" and the "mtos" are available - `appwindow_formspec_func(launcher_app, app, mtos)`- Only custom launcher app: App background / Window decorations and buttons -- `receive_fields_func(app, mtos, fields, sender)` Function for input processing. The "app" and the "mtos" are available inside the call +- `receive_fields_func(app, mtos, sender, fields)` Function for input processing. The "app" and the "mtos" are available inside the call `laptop.register_view(internal_shortname, { definitiontable })` - add a new app or view same as register_app, but the view flag is set. app_name and app_icon not necessary @@ -78,3 +79,12 @@ Definitiontable: `function laptop.get_theme(theme_name)` - `theme:get_button(area, prefix, code, text)` get a themed [prefix]_button in area 'x,y;w,h' with code an text - `theme:get_label(pos, text)` get a themed label text starting at pos 'x,y' + +## Low-level Removable data +`data = mtos:get_removable_data()` +- `label` - Meda label. Item name by default +- `def` - Registered item definition (read-only) +- `inv` - node inventory +- `stack` - The item stack +- `meta` - Stack metadata +- `os_format`- The format type: "none", "OldOS", "backup", "filesystem" (read-only) diff --git a/app_fw.lua b/app_fw.lua index 4fc6c40..290219c 100644 --- a/app_fw.lua +++ b/app_fw.lua @@ -29,16 +29,20 @@ function app_class:get_formspec() end -- internally used: process input -function app_class:receive_fields(fields, sender) +function app_class:receive_data(method, reshow, sender, ...) local ret - if self.receive_fields_func then - ret = self.receive_fields_func(self, self.os, fields, sender) + + if self[method] then + ret = self[method](self, self.os, sender, ...) end - if fields.os_back then - self:back_app() - elseif fields.os_exit then - self:exit_app() + if method == "receive_fields_func" then + local fields = ... + if fields.os_back then + self:back_app() + elseif fields.os_exit then + self:exit_app() + end end return ret end diff --git a/demo_apps.lua b/demo_apps.lua index c96f03e..8b0cf21 100644 --- a/demo_apps.lua +++ b/demo_apps.lua @@ -6,7 +6,7 @@ laptop.register_app("demo1", { formspec_func = function(app, mtos) return mtos.theme:get_button('5,5;3,1', 'major', 'next', 'Second screen') end, - receive_fields_func = function(app, mtos, fields, sender) + receive_fields_func = function(app, mtos, sender, fields) if fields.next then mtos:set_app("demo1_view2") end @@ -18,7 +18,7 @@ laptop.register_view("demo1_view2", { formspec_func = function(app, mtos) return mtos.theme:get_label('1,5', "Use the framework buttons to navigate back or cancel") end, - receive_fields_func = function(app, mtos, fields, sender) + receive_fields_func = function(app, mtos, sender, fields) end }) @@ -32,7 +32,7 @@ laptop.register_app("demo2", { return 'button[3,1;5,1;count;Click: '..data.counter..']'.. 'button[3,3;5,1;back;Back to launcher]' end, - receive_fields_func = function(app, mtos, fields, sender) + receive_fields_func = function(app, mtos, sender, fields) if fields.count then local data = app:get_storage_ref() data.counter = data.counter + 1 diff --git a/init.lua b/init.lua index 41e6078..2420976 100644 --- a/init.lua +++ b/init.lua @@ -5,6 +5,7 @@ dofile(minetest.get_modpath('laptop')..'/app_fw.lua') dofile(minetest.get_modpath('laptop')..'/os.lua') dofile(minetest.get_modpath('laptop')..'/node_fw.lua') dofile(minetest.get_modpath('laptop')..'/nodes.lua') +dofile(minetest.get_modpath('laptop')..'/items.lua') -- uncomment this line to disable demo apps in production --dofile(minetest.get_modpath('laptop')..'/demo_apps.lua') diff --git a/items.lua b/items.lua new file mode 100644 index 0000000..7336c45 --- /dev/null +++ b/items.lua @@ -0,0 +1,14 @@ + +minetest.register_craftitem("laptop:floppy", { + description = 'High density floppy', + inventory_image = "laptop_diskette.png", + groups = {laptop_removable_floppy = 1}, + stack_max = 1, +}) + +minetest.register_craftitem("laptop:usbstick", { + description = 'USB storage stick', + inventory_image = "laptop_usb.png", + groups = {laptop_removable_usb = 1}, + stack_max = 1, +}) diff --git a/node_fw.lua b/node_fw.lua index 291052d..a96b3b2 100644 --- a/node_fw.lua +++ b/node_fw.lua @@ -2,29 +2,29 @@ laptop.node_config = {} local function after_place_node(pos, placer, itemstack, pointed_thing) - local appdata = minetest.deserialize(itemstack:get_meta():get_string("laptop_appdata")) - if appdata then - local mtos = laptop.os_get(pos) - mtos.appdata = appdata - mtos.appdata.launcher = mtos.appdata.launcher or {} - mtos.appdata.os = mtos.appdata.os or {} - mtos.appdata.os.stack = mtos.appdata.os.stack or {} - mtos:save() + local save = minetest.deserialize(itemstack:get_meta():get_string("laptop_metadata")) + if save then + local meta = minetest.get_meta(pos) + meta:from_table(save.fields) + meta:get_inventory():set_list("main", save.invlist) end end local function after_dig_node(pos, oldnode, oldmetadata, digger) - local appdata = oldmetadata.fields['laptop_appdata'] - if appdata then - local item_name = minetest.registered_items[oldnode.name].drop or oldnode.name - local inventory = digger:get_inventory() - for idx = inventory:get_size("main"), 1, -1 do - local stack = inventory:get_stack("main", idx) - if stack:get_name() == item_name and stack:get_meta():get_string("laptop_appdata") == "" then - stack:get_meta():set_string("laptop_appdata", appdata) - digger:get_inventory():set_stack("main", idx, stack) - break - end + local save = { fields = oldmetadata.fields, invlist = {} } + if oldmetadata.inventory and oldmetadata.inventory.main then + for _, stack in ipairs(oldmetadata.inventory.main) do + table.insert(save.invlist, stack:to_string()) + end + end + + local item_name = minetest.registered_items[oldnode.name].drop or oldnode.name + local inventory = digger:get_inventory() + for idx, stack in ipairs(inventory:get_list("main")) do + if stack:get_name() == item_name and stack:get_meta():get_string("laptop_metadata") == "" then + stack:get_meta():set_string("laptop_metadata", minetest.serialize(save)) + digger:get_inventory():set_stack("main", idx, stack) + break end end end @@ -45,6 +45,7 @@ local function on_construct(pos) end local function on_punch(pos, node, puncher) + -- TODO: Check if wielded item is an removable. Replace stack in this case local mtos = laptop.os_get(pos) local hwdef = laptop.node_config[node.name] if hwdef.next_node then @@ -61,7 +62,47 @@ end local function on_receive_fields(pos, formname, fields, sender) local mtos = laptop.os_get(pos) - mtos:receive_fields(fields, sender) + mtos:pass_to_app("receive_fields_func", true, sender, fields) +end + +local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) + local mtos = laptop.os_get(pos) + return mtos:pass_to_app("allow_metadata_inventory_move", false, player, from_list, from_index, to_list, to_index, count) or 0 +end + +local function allow_metadata_inventory_put(pos, listname, index, stack, player) + local mtos = laptop.os_get(pos) + local def = stack:get_definition() + local allowed_stacksize = 0 + if def then + local supported = mtos.hwdef.removable_capability or {"usb", "floppy"} + for _, cap in ipairs(supported) do + if def.groups["laptop_removable_"..cap] then + allowed_stacksize = 1 + end + end + end + return mtos:pass_to_app("allow_metadata_inventory_put", false, player, listname, index, stack) or allowed_stacksize +end + +local function allow_metadata_inventory_take(pos, listname, index, stack, player) + local mtos = laptop.os_get(pos) + return mtos:pass_to_app("allow_metadata_inventory_take", false, player, listname, index, stack) or 1 -- by default removal allowed +end + +local function on_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) + local mtos = laptop.os_get(pos) + mtos:pass_to_app("on_metadata_inventory_move", true, player, from_list, from_index, to_list, to_index, count) +end + +local function on_metadata_inventory_put(pos, listname, index, stack, player) + local mtos = laptop.os_get(pos) + mtos:pass_to_app("on_metadata_inventory_put", true, player, listname, index, stack) +end + +local function on_metadata_inventory_take(pos, listname, index, stack, player) + local mtos = laptop.os_get(pos) + mtos:pass_to_app("on_metadata_inventory_take", true, player, listname, index, stack) end function laptop.register_hardware(name, hwdef) @@ -89,6 +130,12 @@ function laptop.register_hardware(name, hwdef) def.on_punch = on_punch def.on_construct = on_construct def.on_receive_fields = on_receive_fields + def.allow_metadata_inventory_move = allow_metadata_inventory_move + def.allow_metadata_inventory_put = allow_metadata_inventory_put + def.allow_metadata_inventory_take = allow_metadata_inventory_take + def.on_metadata_inventory_move = on_metadata_inventory_move + def.on_metadata_inventory_put = on_metadata_inventory_put + def.on_metadata_inventory_take = on_metadata_inventory_take minetest.register_node(nodename, def) -- set node configuration for hooks diff --git a/os.lua b/os.lua index 25052fa..1b3ccfc 100644 --- a/os.lua +++ b/os.lua @@ -10,31 +10,30 @@ local mod_storage = minetest.get_mod_storage() -- Swap the node function os_class:swap_node(new_node_name) local node = minetest.get_node(self.pos) - node.name = new_node_name + if new_node_name then + node.name = new_node_name + end + local fdir = math.floor(node.param2 % 32) + node.param2 = fdir + self.theme.node_color * 32 + print("swap to", dump(node)) --TODO:remove minetest.swap_node(self.pos, node) end -- Power on the system and start the launcher function os_class:power_on(new_node_name) - if new_node_name then - self:swap_node(new_node_name) - end + self:swap_node(new_node_name) self:set_app() --launcher end -- Power on the system / and resume last running app function os_class:resume(new_node_name) - if new_node_name then - self:swap_node(new_node_name) - end + self:swap_node(new_node_name) self:set_app(self.appdata.os.current_app) end -- Power off the system function os_class:power_off(new_node_name) - if new_node_name then - self:swap_node(new_node_name) - end + self:swap_node(new_node_name) self.meta:set_string('formspec', "") self:save() end @@ -44,17 +43,6 @@ function os_class:set_infotext(infotext) self.meta:set_string('infotext', infotext) end --- Save the data -function os_class:save() - self.meta:set_string('laptop_appdata', minetest.serialize(self.appdata)) - if self.cloud_store then - for store, value in pairs(self.cloud_store) do - mod_storage:set_string(store, minetest.serialize(value)) - end - self.cloud_store = nil - end -end - -- Get given or current theme function os_class:get_theme(theme) local theme_sel = theme or self.appdata.os.theme @@ -67,6 +55,7 @@ function os_class:set_theme(theme) self.appdata.os.theme = theme self.theme = self:get_theme() self:save() + self:swap_node() end end @@ -124,18 +113,65 @@ function os_class:set_app(appname) end -- Handle input processing -function os_class:receive_fields(fields, sender) +function os_class:pass_to_app(method, reshow, sender, ...) local appname = self.appdata.os.current_app or self.hwdef.custom_launcher or "launcher" local app = self:get_app(appname) - app:receive_fields(fields, sender) + local ret = app:receive_data(method, reshow, sender, ...) self.appdata.os.last_player = sender:get_player_name() - if self.appdata.os.current_app == appname then + if self.appdata.os.current_app == appname and reshow then local formspec = app:get_formspec() if formspec ~= false then self.meta:set_string('formspec', formspec) end end self:save() + return ret +end + +-- Get Low-Level access to inventory slot +function os_class:get_removable_data(media_type) + self.removable_data = nil + local inv = self.meta:get_inventory() + inv:set_size("main", 1) -- 1 disk supported + local stack = inv:get_stack("main", 1) + if stack then + local def = stack:get_definition() + if def and def.name ~= "" then + local stackmeta = stack:get_meta() + local os_format = stackmeta:get_string("os_format") + if os_format == "" then + os_format = "none" + end + if not media_type or -- no parameter asked + def.groups["laptop_removable_"..media_type] or --usb or floppy + media_type == os_format then -- "none", "OldOS", "backup", "filesystem" + local data = { + inv = inv, + def = def, + stack = stack, + meta = stackmeta, + os_format = os_format, + } + data.label = data.meta:get_string("description") + if not data.label or data.label == "" then + data.label = def.description + end + self.removable_data = data + return data + end + end + end +end + +-- Store data to inventory slot item (low-level) +function os_class:set_removable_data() + if self.removable_data then + local data = self.removable_data + if data.label ~= data.def.description then + data.meta:set_string("description", data.label) + end + data.inv:set_stack("main", 1, data.stack) + end end -- Get mod storage as (=internet / cloud) @@ -146,6 +182,44 @@ function os_class:connect_to_cloud(store_name) return self.cloud_store[store_name] end +-- Connect to app storage on removable +function os_class:connect_to_removable(store_name) + local data = self.removable_data or self:get_removable_data("data") + if not data then + self.removable_app_data = nil + return + end + self.removable_app_data = self.removable_app_data or {} + self.removable_app_data[store_name] = self.removable_app_data[store_name] or + minetest.deserialize(data.meta:get_string(store_name)) or {} + return self.cloud_store[store_name] +end + +-- Save the data +function os_class:save() + -- Nodemata (HDD) + self.meta:set_string('laptop_appdata', minetest.serialize(self.appdata)) + + -- Modmeta (Cloud) + if self.cloud_store then + for store, value in pairs(self.cloud_store) do + mod_storage:set_string(store, minetest.serialize(value)) + end + self.cloud_store = nil + end + + -- Itemstack meta (removable) + if self.removable_data then + if self.removable_app_data then + for store, value in pairs(self.removable_app_data) do + data.meta:set_string(store, minetest.serialize(value)) + end + end + self:set_removable_data() + end + self.removable_data = nil +end + ----------------------------------------------------- -- Get Operating system object ----------------------------------------------------- diff --git a/themes.lua b/themes.lua index 10c4884..eb566e6 100644 --- a/themes.lua +++ b/themes.lua @@ -8,6 +8,7 @@ laptop.themes = { exit_button = "laptop_theme_freedom_exit_button.png", app_button = "laptop_theme_freedom_app_button.png", textcolor = "#000000", + node_color = 0, }, }