From 721eb267779cc35ae4c8f8949c867668adb4314f Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 18 Dec 2021 13:35:18 -0700 Subject: [PATCH] Got modules basically working in CLI and Minetest, still having issues with Minetest interactions in module:initiate() --- modpol/api.lua | 4 +- modpol/interactions/interactions.lua | 19 ++- modpol/modules/add_child_org.lua | 29 ++++ modpol/modules/child_org.lua | 32 ----- modpol/modules/consent.lua | 50 ++++--- modpol/modules/join_org_consent.lua | 10 +- modpol/modules/remove_org.lua | 23 +-- modpol/modules/remove_org_consent.lua | 28 ++-- modpol/modules/template.lua | 32 +++-- modpol/orgs/base.lua | 6 +- modpol/orgs/process.lua | 39 +++--- modpol_minetest/overrides/interactions.lua | 155 ++++----------------- 12 files changed, 187 insertions(+), 240 deletions(-) create mode 100644 modpol/modules/add_child_org.lua delete mode 100644 modpol/modules/child_org.lua diff --git a/modpol/api.lua b/modpol/api.lua index 8c2bc48..f5e04e6 100644 --- a/modpol/api.lua +++ b/modpol/api.lua @@ -13,7 +13,9 @@ dofile (localdir .. "/orgs/process.lua") dofile (localdir .. "/interactions/interactions.lua") --modules +--TODO make this automatic and directory-based +dofile (localdir .. "/modules/add_child_org.lua") dofile (localdir .. "/modules/consent.lua") dofile (localdir .. "/modules/join_org_consent.lua") dofile (localdir .. "/modules/remove_org_consent.lua") ---dofile (localdir .. "/modules/child_org.lua") +dofile (localdir .. "/modules/remove_org.lua") diff --git a/modpol/interactions/interactions.lua b/modpol/interactions/interactions.lua index 5e2bddf..b198e4f 100644 --- a/modpol/interactions/interactions.lua +++ b/modpol/interactions/interactions.lua @@ -34,17 +34,16 @@ function modpol.interactions.dashboard(user) print('All users: ' .. table.concat(all_users, ', ')) print() - print('Access which org?') - + print('Access which org id?') local sel = io.read() print() if modpol.orgs.array[tonumber(sel)] then local sel_org = modpol.orgs.array[tonumber(sel)].name + modpol.interactions.org_dashboard(user, sel_org) else - return + print("Org id not found.") end - modpol.interactions.org_dashboard(user, sel_org) end @@ -72,8 +71,8 @@ function modpol.interactions.org_dashboard(user, org_name) -- list available modules local org_modules = {} - for k,v in ipairs(org.modules) do - table.insert(org_modules, org.modules[k].slug) + for k,v in pairs(org.modules) do + table.insert(org_modules, v.slug) end -- list pending actions @@ -136,6 +135,7 @@ function modpol.interactions.org_dashboard(user, org_name) end else print("Command not found") + modpol.interactions.org_dashboard(user, org_name) end end @@ -230,6 +230,13 @@ end -- Function: modpol.interactions.message_org -- input: initiator (string), org_id (number), message (string) -- output: broadcasts message to all org members +function modpol.interactions.message_org(initiator, org_id, message) + local org = modpol.orgs.get_org(org_id) + local users = org:list_members() + for k,v in ipairs(users) do + modpol.interactions.message(v, message) + end +end -- Function: modpol.interactions.binary_poll_org -- input: initator (user string), org_id (number) diff --git a/modpol/modules/add_child_org.lua b/modpol/modules/add_child_org.lua new file mode 100644 index 0000000..bad73b3 --- /dev/null +++ b/modpol/modules/add_child_org.lua @@ -0,0 +1,29 @@ +--- @module add_child_org +-- Adds a child org + +local add_child_org = { + name = "Add child org", + slug = "add_child_org", + desc = "Create a child org within the current one" +} +add_child_org.data = { +} +add_child_org.config = { +} + +-- @function initiate +function add_child_org:initiate(result) + modpol.interactions.text_query( + self.initiator,"Child org name: ", + function(input) + self.org:add_org(input, self.initiator) + modpol.interactions.message_org( + self.initiator, + self.org.id, + "Child org created: "..input) + modpol.interactions.dashboard(self.initiator) + end) +end + +--- (Required) Add to module table +modpol.modules.add_child_org = add_child_org diff --git a/modpol/modules/child_org.lua b/modpol/modules/child_org.lua deleted file mode 100644 index 8a1631a..0000000 --- a/modpol/modules/child_org.lua +++ /dev/null @@ -1,32 +0,0 @@ --- CHILD_ORG --- Module that enables user to create child in current org - --- Initial configuration -modpol.modules.child_org = {} -modpol.modules.child_org.name = "Create child org" - - --- == REQUEST == - --- Initialization function --- function: modpol.modules.child_org:initialize - - --- gather data from initiator: child org name, comment - --- function: modpol.modules.child_org:request - - - --- == CONSENT == - --- function: modpol.orgs:consent(process_id, result_function) --- the result function should begin the completion process below - - - --- == COMPLETION == - --- if approved/if failed functions? - --- function: modpol.modules.child_org.create(initiator, parent_org, child_org) diff --git a/modpol/modules/consent.lua b/modpol/modules/consent.lua index e7d22b3..03983a6 100644 --- a/modpol/modules/consent.lua +++ b/modpol/modules/consent.lua @@ -1,42 +1,52 @@ +--- @module consent +-- A utility module for checking consent - -Consent = {} - -Consent.setup = { +local consent = { name = "Consent", slug = "consent", desc = "Other modules can use to implement consent based decision making", +} + +consent.data = { votes = 0 } -Consent.config = { - prompt = "Would you like to approve this action?", +consent.config = { + prompt = "Do you consent?", votes_required = 1 } -function Consent:initiate(result) - self.result = result - for id, member in pairs(self.org.members) do - self.org:add_pending_action(self.id, member, "callback") - end +function consent:initiate(result) + self.result = result + -- if org is empty, consent is given automatically + if self.org:get_member_count() == 0 then + self.result() + self.org:wipe_pending_actions(self.id) + else + -- otherwise, create poll + for id, member in pairs(self.org.members) do + self.org:add_pending_action(self.id, member, "callback") + end + end end -function Consent:callback(member) +function consent:callback(member) modpol.interactions.binary_poll_user( member, self.config.prompt, function (resp) - if resp == "Yes" then - self.votes = self.votes + 1 - end + self.org:remove_pending_action(self.id,member) + if resp == "Yes" then + self.data.votes = self.data.votes + 1 + end - if self.votes >= self.config.votes_required then - self.result() - self.org:wipe_pending_actions(self.id) - end + if self.data.votes >= self.config.votes_required then + if self.result then self.result() end + self.org:wipe_pending_actions(self.id) + end end ) end -modpol.modules.consent = Consent +modpol.modules.consent = consent diff --git a/modpol/modules/join_org_consent.lua b/modpol/modules/join_org_consent.lua index d126fd5..9228dc7 100644 --- a/modpol/modules/join_org_consent.lua +++ b/modpol/modules/join_org_consent.lua @@ -2,14 +2,18 @@ -- A simple module that calls a consent process on an org to add a member. -- Depends on the Consent module. -join_org_consent = {} - -join_org_consent.setup = { +local join_org_consent = { name = "Join this org", slug = "join_org_consent", desc = "Adds member with consent of all members." } +join_org_consent.data = { +} + +join_org_consent.config = { +} + function join_org_consent:initiate() self.org:call_module( "consent", diff --git a/modpol/modules/remove_org.lua b/modpol/modules/remove_org.lua index ae9bbdc..590771d 100644 --- a/modpol/modules/remove_org.lua +++ b/modpol/modules/remove_org.lua @@ -1,22 +1,27 @@ ---- Remove Org +--- @module Remove Org -- A simple module that calls a consent process on an org to remove it. --- Depends on the Consent module. -remove_org = {} ---- (Required): setup table containing name and description of the module -remove_org.setup = { + +--- Main module table +remove_org = { name = "Remove this org", slug = "remove_org", desc = "Removes an org if all members consent." } +remove_org.config = {} +remove_org.data = {} + --- Initiate function ---
  • self.org (the org the module was called in),
  • ---
  • self.initiator (the user that callced the module),
  • ---
  • self.id (the process id of the module instance)
  • -- @function initiate function remove_org:initiate(config, result) - + modpol.interactions.message_org( + self.initiator,self.org.id, + "Removing org: "..self.org.name) + self.org:delete() + modpol.interactions.dashboard(self.initiator) -- call result function if result then result() end end + +modpol.modules.remove_org = remove_org diff --git a/modpol/modules/remove_org_consent.lua b/modpol/modules/remove_org_consent.lua index 9574901..de5c305 100644 --- a/modpol/modules/remove_org_consent.lua +++ b/modpol/modules/remove_org_consent.lua @@ -1,31 +1,39 @@ --- Remove org (consent) -- A simple module that calls a consent process on an org to remove it. -- Depends on the Consent module. -remove_org_consent = {} -remove_org_consent.setup = { +local remove_org_consent = { name = "Remove this org", - slug = "remove_org", + slug = "remove_org_consent", desc = "Removes an org if all members consent." } +remove_org_consent.data = { +} + +remove_org_consent.config = { +} + function remove_org_consent:initiate() self.org:call_module( "consent", self.initiator, { - prompt = "Remove org " .. self.org.name .. "?", - votes_required = #self.org.members + prompt = "Remove org " .. self.org.name .. "?", + votes_required = #self.org.members }, - function () - self:complete() + function () + self:complete() end ) end -function join_org_consent:complete() - self.org:delete() - print("Removed org " .. self.org.name .. ".") +function remove_org_consent:complete() + modpol.interactions.message_org( + self.initiator, self.org.id, + "Removing org: " .. self.org.name) + self.org:delete() + modpol.interactions.dashboard(self.initiator) end modpol.modules.remove_org_consent = remove_org_consent diff --git a/modpol/modules/template.lua b/modpol/modules/template.lua index 777f8f6..7358e57 100644 --- a/modpol/modules/template.lua +++ b/modpol/modules/template.lua @@ -1,26 +1,31 @@ ---- Template for modules --- function initiate and table setup are required as a base. --- @module Template -Template = {} +--- module_template +-- @module module_template ---- (Required): setup table containing name and description of the module --- @field name "Module Human-Readable Name" --- @field slug "Template" same as module name +--- (Required): data table containing name and description of the module +-- @field name "Human-readable name" +-- @field slug "Same as module class name" -- @field desc "Description of the module" -Template.setup = { +local module_template = { name = "Module Human-Readable Name", - slug = "Template", + slug = "template", desc = "Description of the module" } ---- (Optional): config for module +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +module_template.data = { +} + +--- (Required): config for module -- Defines the input parameters to the module initiate function. +-- Can be blank -- When calling a module from within another module, -- variables not defined in config will be ignored. -- Default values set in config can be overridden -- @field field_1 ex: votes_required, default = 5 -- @field field_2 ex: voting_type, default = "majority" -Template.config = { +module_template.config = { field_1 = 5 field_2 = "majority" } @@ -33,9 +38,12 @@ Template.config = { -- @param config (optional) If user wants to override fields in the config table -- @param result (optional) Callback if this module is embedded in other modules -- @function initiate -function Template:initiate(config, result) +function module_template:initiate(config, result) -- call interaction functions here! -- call result function if result then result() end end + +--- (Required) Add to module table +modpol.modules.module_template = module_template diff --git a/modpol/orgs/base.lua b/modpol/orgs/base.lua index 2211e6e..27acf66 100644 --- a/modpol/orgs/base.lua +++ b/modpol/orgs/base.lua @@ -1,3 +1,6 @@ +--- Orgs: Base +-- Basic functions for orgs + modpol.orgs = modpol.orgs or { count = 1, @@ -171,7 +174,8 @@ function modpol.orgs:add_org(name, user) child_org.id = modpol.orgs.count child_org.name = name child_org.parent = self.id - child_org.processes = self.processes + child_org.processes = {} + child_org.modules = self.modules setmetatable(child_org, modpol.orgs) diff --git a/modpol/orgs/process.lua b/modpol/orgs/process.lua index 38c10b6..8f20761 100644 --- a/modpol/orgs/process.lua +++ b/modpol/orgs/process.lua @@ -1,6 +1,8 @@ -function modpol.orgs:call_module(module_name, initiator, config, result) - if not modpol.modules[module_name] then - modpol.ocutil.log('Error in ' .. self.name .. ':call_module -> module "' .. module_name .. '" not found') +--- Process functions for orgs + +function modpol.orgs:call_module(module_slug, initiator, config, result) + if not modpol.modules[module_slug] then + modpol.ocutil.log('Error in ' .. self.name .. ':call_module -> module "' .. module_slug .. '" not found') return end @@ -21,10 +23,10 @@ function modpol.orgs:call_module(module_name, initiator, config, result) index = #self.processes + 1 end - local module = modpol.modules[module_name] + local module = modpol.modules[module_slug] -- sets default values for undeclared config variables - if module.config then + if #module.config > 0 then for k, v in pairs(module.config) do if config[k] == nil then config[k] = v @@ -39,14 +41,10 @@ function modpol.orgs:call_module(module_name, initiator, config, result) org = self, id = index, config = config, - name = module_name + data = module.data, + slug = module_slug } - -- copying default fields from setup - for k, v in pairs(module.setup) do - new_process[k] = v - end - setmetatable(new_process, new_process.metatable) self.processes[index] = new_process @@ -76,7 +74,7 @@ function modpol.orgs:wipe_pending_actions(process_id) end end -function modpol.orgs:has_pending_actions() +function modpol.orgs:has_pending_actions(user) -- next() will return the next pair in a table -- if next() returns nil, the table is empty if not self.pending[user] then @@ -91,11 +89,14 @@ function modpol.orgs:has_pending_actions() end function modpol.orgs:interact(process_id, user) - local process = self.processes[process_id] - if self.pending[user] then - local callback = self.pending[user][process_id] - if callback then - process[callback](process, user) - end - end + local process = self.processes[process_id] + modpol.interactions.message(user,"hi!") + if self.pending[user] then + modpol.interactions.message(user,"id: "..process_id) + local callback = self.pending[user][process_id] + if callback then + modpol.interactions.message(user,"la!") + process[callback](process, user) + end + end end diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index b109a36..5a12989 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -59,10 +59,7 @@ function modpol.interactions.dashboard(user) "dropdown[2,2.5;5,0.8;user_orgs;"..formspec_list(user_orgs)..";;]", "label[0.5,4;All users:]", "dropdown[2,3.5;5,0.8;all_users;"..formspec_list(all_users)..";;]", - "button[0.5,7;1,0.8;test_poll;Test poll]", - "button[2,7;1,0.8;add_org;Add org]", - "button[3.5,7;1.5,0.8;remove_org;Remove org]", - "button[5.5,7;1.5,0.8;reset_orgs;Reset orgs]", + "button[0.5,7;1.5,0.8;reset_orgs;Reset orgs]", "button_exit[8.5,7;1,0.8;close;Close]", } local formspec_string = table.concat(formspec, "") @@ -75,22 +72,6 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) local pname = player:get_player_name() if nil then -- buttons first - elseif fields.test_poll then - -- FOR TESTING PURPOSES ONLY - modpol.interactions.text_query( - pname,"Poll question:", - function(input) - modpol.interactions.binary_poll_user( - pname, input, - function(vote) - modpol.interactions.message( - pname, pname .. " voted " .. vote) - end) - end) - elseif fields.add_org then - modpol.interactions.add_org(pname, 1) - elseif fields.remove_org then - modpol.interactions.remove_org(pname) elseif fields.reset_orgs then modpol.orgs.reset() modpol.instance:add_member(pname) @@ -144,18 +125,20 @@ function modpol.interactions.org_dashboard(user, org_name) -- prepare modules menu local modules = {"View..."} if org.modules then - for k,v in ipairs(org.modules) do - table.insert(modules, org.modules[k].slug) + for k,v in pairs(org.modules) do + table.insert(modules, v.slug) end end -- prepare actions menu local actions = {"View..."} + local num_actions = 0 if org.pending[user] then for k,v in pairs(org.pending[user]) do local action_string = "[" .. k .. "] " .. - org.processes[k].name + org.processes[k].name table.insert(actions, action_string) + num_actions = num_actions + 1 end end @@ -177,7 +160,7 @@ function modpol.interactions.org_dashboard(user, org_name) "dropdown[2,2.5;5,0.8;children;"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", "dropdown[2,3.5;5,0.8;modules;"..formspec_list(modules)..";;]", - "label[0.5,5;Actions:]", + "label[0.5,5;Actions ("..num_actions.."):]", "dropdown[2,4.5;5,0.8;actions;"..formspec_list(actions)..";;]", "button[0.5,7;1,0.8;test_poll;Test poll]", "button[2,7;1,0.8;add_child;Add child]", @@ -195,12 +178,7 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) local org = modpol.orgs.get_org(_contexts[pname].current_org) if nil then elseif fields.join then - local new_request = { - user = pname, - type = "add_member", - params = {pname} - } - org:make_request(new_request) + org:add_member(pname) modpol.interactions.org_dashboard(pname,org.name) elseif fields.leave then org:remove_member(pname) @@ -217,24 +195,19 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) elseif fields.add_child then modpol.interactions.text_query( pname, "Child org name:", - function(input) - local new_request = { - user = pname, - type = "add_org", - params = {input} - } - org:make_request(new_request) - modpol.interactions.message(pname,"requested") - modpol.interactions.org_dashboard( - pname,org.name) + function(input) + org:add_org(input,pname) + modpol.interactions.message_org( + pname, + org.id, + "Child org created: " .. input) end) elseif fields.remove_org then - local new_request = { - user = pname, - type = "delete", - params = {} - } - org:make_request(new_request) + modpol.interactions.message_org( + pname, + org.id, + "Removing org: " .. org.name) + org:delete() modpol.interactions.org_dashboard(pname,org.name) elseif fields.back then modpol.interactions.dashboard(pname) @@ -244,8 +217,9 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) elseif fields.modules and fields.modules ~= "View..." then local module = fields.modules - org:call_module(module, user) - + org:call_module(module, pname) + modpol.interactions.org_dashboard(pname,org.name) + -- Receiving actions elseif fields.actions and fields.actions ~= "View..." then @@ -253,7 +227,7 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) fields.actions,"%[(%d)%]") local process = org.processes[tonumber(action)] if process then - org:interact(process, user) + org:interact(process.id, pname) end -- Children @@ -282,8 +256,8 @@ end -- =========================== -- Function: modpol.interactions.message --- input: message (string) --- output +-- input: user (string), message (string) +-- output: displays message to specified user function modpol.interactions.message(user, message) minetest.chat_send_player(user, message) end @@ -291,7 +265,6 @@ end -- Function: modpol.interactions.text_query -- Overrides function at modpol/interactions.lua -- input: user (string), query (string), func (function) --- func input: user input (string) -- output: Applies "func" to user input function modpol.interactions.text_query(user, query, func) -- set up formspec @@ -303,7 +276,7 @@ function modpol.interactions.text_query(user, query, func) "button[0.5,2.5;1,0.8;yes;OK]", } local formspec_string = table.concat(formspec, "") - -- present to players + -- present to player minetest.show_formspec(user, "modpol:text_query", formspec_string) -- put func in _contexts if _contexts[user] == nil then _contexts[user] = {} end @@ -367,10 +340,6 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) end end) - --- SECONDARY INTERACTIONS --- ====================== - -- Function: modpol.binary_poll_user(user, question, function) -- Overrides function at modpol/interactions.lua -- Params: user (string), question (string), func (function) @@ -384,8 +353,8 @@ function modpol.interactions.binary_poll_user(user, question, func) "label[0.375,0.5;",minetest.formspec_escape(question), "]", "button[1,1.5;1,0.8;yes;Yes]", "button[2,1.5;1,0.8;no;No]", - --TKTK can we enable text wrapping? - --TKTK we could use scroll boxes to contain the text + --TODO can we enable text wrapping? + --TODO we could use scroll boxes to contain the text } local formspec_string = table.concat(formspec, "") if _contexts[user] == nil then _contexts[user] = {} end @@ -409,71 +378,3 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) minetest.close_formspec(pname, formname) end end) - --- COMPLEX INTERACTIONS --- ==================== - --- Function: modpol.interactions.message_org --- input: initiator (string), org_id (number), message (string) --- output: broadcasts message to all org members -function modpol.interactions.message_org(initiator, org_id, message) - local org = modpol.orgs.get_org(org_id) - local users = org:list_members() - for k,v in ipairs(users) do - modpol.interactions.message(v, message) - end -end - - --- Function: modpol.interactions.binary_poll_org --- input: initator (user string), org_id (number) --- output: gets question from initiator, asks all org members, broadcasts answers --- TODO for testing. This should be implemented as a request. -function modpol.interactions.binary_poll_org(initiator, org_id, func) - local org = modpol.orgs.get_org(org_id) - local users = org:list_members() - modpol.interactions.text_query( - initiator, "Yes/no poll question:", - function(input) - for k,v in ipairs(users) do - modpol.interactions.binary_poll_user(v, input, func) - end - end) -end - --- Function: modpol.interactions.add_org --- input: initator (user string), base_org_id (ID) --- output: interaction begins --- GODMODE -function modpol.interactions.add_org(user, base_org_id) - modpol.interactions.text_query( - user,"Org name:", - function(input) - local base_org = modpol.orgs.get_org(1) - local result = base_org:add_org(input, user) - local message = input .. " created" - modpol.interactions.message(user, message) - modpol.interactions.dashboard(user) - end) -end - --- Function: modpol.interactions.remove_org --- input: initator (user string) --- output: interaction begins --- GODMODE -function modpol.interactions.remove_org(user) - -- start formspec - local orgs_list = modpol.orgs.list_all() - local label = "Choose an org to remove:" - modpol.interactions.dropdown_query( - user, label, orgs_list, - function(input) - if input then - local target_org = modpol.orgs.get_org(input) - local result = target_org:delete() - local message = input .. " deleted" - modpol.interactions.message(user, message) - end - modpol.interactions.dashboard(user) - end) -end