From 9a2c72c6a1f7808c9ae4739a18c6836c35bd1e07 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Tue, 21 Dec 2021 18:12:59 -0700 Subject: [PATCH 01/26] Bugfixes after test with Skylar! --- mod.conf | 2 +- modpol_core/api.lua | 1 + modpol_core/interactions/interactions.lua | 8 +++--- modpol_core/modules/add_child_org_consent.lua | 3 ++- modpol_core/modules/change_modules.lua | 2 +- modpol_core/modules/consent.lua | 4 +-- modpol_core/orgs/base.lua | 6 +++-- modpol_core/orgs/users.lua | 27 +++++++++++++++++++ modpol_core/util/misc.lua | 3 ++- modpol_minetest/api.lua | 1 + modpol_minetest/chatcommands.lua | 1 - modpol_minetest/overrides/interactions.lua | 9 ++++--- modpol_minetest/overrides/users.lua | 20 ++++++++++++++ 13 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 modpol_core/orgs/users.lua create mode 100644 modpol_minetest/overrides/users.lua diff --git a/mod.conf b/mod.conf index 0bc981d..5d32d27 100644 --- a/mod.conf +++ b/mod.conf @@ -1,7 +1,7 @@ name = modpol title = Modpol author = ntnsndr -description = Framework that enables diverse governance processes +description = A framework that enables diverse governance processes license = MIT forum = https://forum.minetest.net/viewtopic.php?f=9&t=27598 version = 0.1 diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 87776d0..8e7ebd4 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -5,6 +5,7 @@ local localdir = modpol.topdir --orgs dofile (localdir .. "/orgs/base.lua") dofile (localdir .. "/orgs/process.lua") +dofile (localdir .. "/orgs/users.lua") --interactions dofile (localdir .. "/interactions/interactions.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index f3df03c..501dc11 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -37,8 +37,10 @@ function modpol.interactions.dashboard(user) local user_pending_count = 0 for k,v in ipairs(modpol.orgs.array) do if v.pending and v.pending[user] then - table.insert(user_pending, v.name) - user_pending_count = user_pending_count + 1 + if modpol.util.num_pairs(v.pending[user]) ~= 0 then + table.insert(user_pending, v.name) + user_pending_count = user_pending_count + 1 + end end end @@ -108,7 +110,7 @@ function modpol.interactions.org_dashboard(user, org_string) print("Org: " .. org.name) print("Parent: " .. parent) print("Members: " .. table.concat(org.members, ", ")) - print("Children: " .. table.concat(children, ", ")) + print("Child orgs: " .. table.concat(children, ", ")) print("Modules: " .. table.concat(modules, ", ")) print("Pending: " .. process_msg) print() diff --git a/modpol_core/modules/add_child_org_consent.lua b/modpol_core/modules/add_child_org_consent.lua index e323e19..c3ff403 100644 --- a/modpol_core/modules/add_child_org_consent.lua +++ b/modpol_core/modules/add_child_org_consent.lua @@ -66,7 +66,8 @@ function add_child_org_consent:create_child_org() modpol.interactions.message_org( self.initiator, self.org.name, - "Child org created: "..self.data.child_name) + "Consent reached: created child org " + ..self.data.child_name) if self.data.result then self.data.result() end self.org:delete_process(self.id) end diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index 7b15417..9e2fe14 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -83,7 +83,7 @@ function change_modules:remove_module() local available_modules = {} for k,org_mod in pairs(self.org.modules) do if not org_mod.hide then - available_modules[org_mod.slug] = modpol.copy_table(org_mod) + available_modules[org_mod.slug] = modpol.util.copy_table(org_mod) end end local modules_list = {} local modules_count = 0 diff --git a/modpol_core/modules/consent.lua b/modpol_core/modules/consent.lua index 78deca1..ce6b65f 100644 --- a/modpol_core/modules/consent.lua +++ b/modpol_core/modules/consent.lua @@ -2,9 +2,9 @@ -- A utility module for checking consent local consent = { - name = "Consent process utility", + name = "Consent process", slug = "consent", - desc = "A module other modules use for consent decisions", + desc = "A utility module other modules use for consent decisions", hide = true } diff --git a/modpol_core/orgs/base.lua b/modpol_core/orgs/base.lua index 850cce1..6cd2227 100644 --- a/modpol_core/orgs/base.lua +++ b/modpol_core/orgs/base.lua @@ -72,6 +72,8 @@ end -- =========================================== -- deletes all orgs except for the instance function modpol.orgs.reset() + local instance_members = + modpol.util.copy_table(modpol.instance.members) for id, org in ipairs(modpol.orgs.array) do if id > 1 then modpol.orgs.array[id] = "removed" @@ -80,9 +82,9 @@ function modpol.orgs.reset() modpol.orgs.array[1] = nil modpol.instance = modpol.orgs.init_instance() - + modpol.instance.members = instance_members - modpol.ocutil.log('Reset all orgs') + modpol.ocutil.log('All orgs reset') modpol.orgs:record('Resetting all orgs', 'org_reset') end diff --git a/modpol_core/orgs/users.lua b/modpol_core/orgs/users.lua new file mode 100644 index 0000000..da72524 --- /dev/null +++ b/modpol_core/orgs/users.lua @@ -0,0 +1,27 @@ +-- /users.lua +-- User-related functions for Modular Politics + +-- =================================================================== +-- Function: modpol.list_users +-- Params: org +-- Outputs: Table of user names +-- +-- This may be overwritten by the platform-specific interface + +modpol.list_users = function(org) + local users = {} + if (org == nil) then -- no specified org; all players + if modpol.orgs["instance"] + and modpol.orgs["instance"]["members"] then + -- if instance exists and has membership + users = modpol.orgs["instance"]["members"] + else + users = {} + end + else -- if an org is specified + if (modpol.orgs[org] ~= nil) then -- org exists + users = modpol.orgs[org]["members"] + end + end + return users +end diff --git a/modpol_core/util/misc.lua b/modpol_core/util/misc.lua index e213326..e6ca585 100644 --- a/modpol_core/util/misc.lua +++ b/modpol_core/util/misc.lua @@ -10,7 +10,8 @@ function modpol.util.copy_table(t) return t2 end ---- @function modpol.copy_table +--- @function modpol.num_pairs +-- @param t table with pairs -- Returns the number of elements in a pairs table function modpol.util.num_pairs(t) local i = 0 diff --git a/modpol_minetest/api.lua b/modpol_minetest/api.lua index d1ea9d4..21e9a59 100644 --- a/modpol_minetest/api.lua +++ b/modpol_minetest/api.lua @@ -9,6 +9,7 @@ local localdir = minetest.get_modpath("modpol") .. "/modpol_minetest" --overrides dofile (localdir .. "/overrides/interactions.lua") +dofile (localdir .. "/overrides/users.lua") --testing command for "singleplayer" function modpol.msg(text) diff --git a/modpol_minetest/chatcommands.lua b/modpol_minetest/chatcommands.lua index 103de31..34f204e 100644 --- a/modpol_minetest/chatcommands.lua +++ b/modpol_minetest/chatcommands.lua @@ -35,7 +35,6 @@ regchat( privs = {privs=true}, func = function(user) modpol.orgs.reset() - modpol.instance:add_member(user) modpol.interactions.dashboard(user) return true, "Reset orgs" end, diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index b71cf95..3b8d9b9 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -53,9 +53,10 @@ function modpol.interactions.dashboard(user) local user_pending_count = 0 for k,v in ipairs(modpol.orgs.array) do if v.pending and v.pending[user] then - modpol.msg(v.name) - table.insert(user_pending, v.name) - user_pending_count = user_pending_count + 1 + if modpol.util.num_pairs(v.pending[user]) ~= 0 then + table.insert(user_pending, v.name) + user_pending_count = user_pending_count + 1 + end end end @@ -169,7 +170,7 @@ function modpol.interactions.org_dashboard(user, org_string) "label[0.5,1;Parent: "..parent..membership_toggle(parent).."]", "label[0.5,2;Members:]", "dropdown[2,1.5;7,0.8;user_orgs;"..formspec_list(org.members)..";;]", - "label[0.5,3;Children:]", + "label[0.5,3;Child orgs:]", "dropdown[2,2.5;7,0.8;children;"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", "dropdown[2,3.5;7,0.8;modules;"..formspec_list(modules)..";;]", diff --git a/modpol_minetest/overrides/users.lua b/modpol_minetest/overrides/users.lua new file mode 100644 index 0000000..3d25227 --- /dev/null +++ b/modpol_minetest/overrides/users.lua @@ -0,0 +1,20 @@ +-- =================================================================== +-- Function: modpol.list_users(org) +-- Overwrites function at /users.lua +-- Params: +-- if nil, lists instance members; if an org name, lists its members +-- Output: a table with names of players currently in the game +modpol.list_users = function(org) + local users = {} + if (org == nil) then -- no specified org; all players + for _,player in ipairs(minetest.get_connected_players()) do + local name = player:get_player_name() + table.insert(users,name) + end + else -- if an org is specified + if (modpol.orgs[org] ~= nil) then -- org exists + users = modpol.orgs[org]["members"] + end + end + return users + end From fa50a599381be1d0da2a61b5a0014350260f9889 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 22 Dec 2021 16:28:28 -0700 Subject: [PATCH 02/26] Added randomizer utility module --- modpol_core/api.lua | 1 + modpol_core/modules/randomizer.lua | 50 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 modpol_core/modules/randomizer.lua diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 8e7ebd4..3a935d3 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -18,6 +18,7 @@ dofile (localdir .. "/modules/consent.lua") dofile (localdir .. "/modules/join_org_consent.lua") dofile (localdir .. "/modules/leave_org.lua") dofile (localdir .. "/modules/message_org.lua") +dofile (localdir .. "/modules/random.lua") dofile (localdir .. "/modules/remove_child_consent.lua") dofile (localdir .. "/modules/remove_member_consent.lua") dofile (localdir .. "/modules/remove_org_consent.lua") diff --git a/modpol_core/modules/randomizer.lua b/modpol_core/modules/randomizer.lua new file mode 100644 index 0000000..1d1d07c --- /dev/null +++ b/modpol_core/modules/randomizer.lua @@ -0,0 +1,50 @@ +--- @module randomizer +-- A utility module that outputs a random result from a set of options + +local randomizer = { + name = "Randomizer", + slug = "randomizer", + desc = "A utility module other modules use for random decisions", + hide = true +} + +randomizer.data = { +} + +-- options_table should be a table of strings +randomizer.config = { + options_table = {}, + num_results = 1, + result_table = {} +} + +function randomizer:initiate(result) + self.data.result = result + self.data.options_table = modpol.util.copy_table(self.config.options_table) + -- if options table is empty, randomizer returns that + if #self.data.options_table == 0 or self.config.num_results == 0 then + if self.data.result then + self.data.result({}) end + self.org:wipe_pending_actions(self.id) + self.org:delete_process(self.id) + else + -- otherwise, choose a random result + self.random_loop() + end +end + +-- returns result_table +function randomizer:random_loop() + self.data.results = 0 + if results == self.config.num_results then + self.data.result(self.data.result_table) + else + math.randomseed(os.time()) + local index = math.random(self.data.options_table) + table.insert(self.data.result_table, self.data.options_table[index]) + table.remove(self.data.options_table, index) + self.data.results = self.data.results + 1 + end +end + +modpol.modules.randomizer = randomizer From 44c3e41b8197696bee27bf0f0b9ef6a1760e13a0 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 22 Dec 2021 22:22:17 -0700 Subject: [PATCH 03/26] Created defer_consent module --- modpol_core/api.lua | 3 +- modpol_core/modules/add_child_org_consent.lua | 1 - modpol_core/modules/consent.lua | 3 +- modpol_core/modules/defer_consent.lua | 57 +++++++++++++++++++ modpol_core/modules/randomizer.lua | 1 - modpol_core/modules/template.lua | 5 +- modpol_core/orgs/process.lua | 3 +- 7 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 modpol_core/modules/defer_consent.lua diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 3a935d3..8c5e4cd 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -15,10 +15,11 @@ dofile (localdir .. "/interactions/interactions.lua") dofile (localdir .. "/modules/add_child_org_consent.lua") dofile (localdir .. "/modules/change_modules.lua") dofile (localdir .. "/modules/consent.lua") +dofile (localdir .. "/modules/defer_consent.lua") dofile (localdir .. "/modules/join_org_consent.lua") dofile (localdir .. "/modules/leave_org.lua") dofile (localdir .. "/modules/message_org.lua") -dofile (localdir .. "/modules/random.lua") +dofile (localdir .. "/modules/randomizer.lua") dofile (localdir .. "/modules/remove_child_consent.lua") dofile (localdir .. "/modules/remove_member_consent.lua") dofile (localdir .. "/modules/remove_org_consent.lua") diff --git a/modpol_core/modules/add_child_org_consent.lua b/modpol_core/modules/add_child_org_consent.lua index c3ff403..4711387 100644 --- a/modpol_core/modules/add_child_org_consent.lua +++ b/modpol_core/modules/add_child_org_consent.lua @@ -26,7 +26,6 @@ function add_child_org_consent:initiate(result) modpol.interactions.org_dashboard( self.initiator, self.org.name) self.org:delete_process(self.id) - if result then result() end return elseif modpol.orgs.get_org(input) then modpol.interactions.message( diff --git a/modpol_core/modules/consent.lua b/modpol_core/modules/consent.lua index ce6b65f..c46806e 100644 --- a/modpol_core/modules/consent.lua +++ b/modpol_core/modules/consent.lua @@ -23,7 +23,7 @@ function consent:initiate(result) if self.org:get_member_count() == 0 then if self.data.result then self.data.result() end - self.org:wipe_pending_actions(self.id) + self.org:delete_process(self.id) else -- otherwise, create poll for id, member in pairs(self.org.members) do @@ -44,7 +44,6 @@ function consent:callback(member) if self.data.votes >= self.config.votes_required then if self.data.result then self.data.result() end - self.org:wipe_pending_actions(self.id) self.org:delete_process(self.id) end modpol.interactions.org_dashboard( diff --git a/modpol_core/modules/defer_consent.lua b/modpol_core/modules/defer_consent.lua new file mode 100644 index 0000000..b931bf8 --- /dev/null +++ b/modpol_core/modules/defer_consent.lua @@ -0,0 +1,57 @@ +--- defer_consent +-- @module defer_consent + +--- (Required): data table containing name and description of the module +-- @field name "Human-readable name (parens OK, no brackets)" +-- @field slug "Same as module class name" +-- @field desc "Description of the module" +-- @field hide "Whether this is a hidden utility module" +local defer_consent = { + name = "Defer consent", + slug = "defer_consent", + desc = "Defers consent on a decision to another org", + hide = true; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +defer_consent.data = { +} + +--- (Required): config for module +-- @field defer_org Name or ID of target org +-- @field votes_required Threshold passed on to `consent` +-- @field prompt String passed on to `consent` +defer_consent.config = { + defer_org = "Root", + votes_required = 1, + prompt = "Do you consent?" +} + +--- (Required): initiate function +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function defer_consent:initiate(result) + local defer_org = modpol.orgs.get_org(self.config.defer_org) + if not defer_org then + modpol.interactions.message( + self.initiator, "Target org not found, aborting") + self.org:delete_process(self.id) + else + defer_org:call_module( + "consent", self.initiator, + { + votes_required = self.config.votes_required, + prompt = self.config.prompt + }, + function() + if result then result() end + end) + end + if result then result() end + self.org:delete_process(self.id) +end + +--- (Required) Add to module table +modpol.modules.defer_consent = defer_consent diff --git a/modpol_core/modules/randomizer.lua b/modpol_core/modules/randomizer.lua index 1d1d07c..fa45851 100644 --- a/modpol_core/modules/randomizer.lua +++ b/modpol_core/modules/randomizer.lua @@ -25,7 +25,6 @@ function randomizer:initiate(result) if #self.data.options_table == 0 or self.config.num_results == 0 then if self.data.result then self.data.result({}) end - self.org:wipe_pending_actions(self.id) self.org:delete_process(self.id) else -- otherwise, choose a random result diff --git a/modpol_core/modules/template.lua b/modpol_core/modules/template.lua index 1bbd03f..1ca63e8 100644 --- a/modpol_core/modules/template.lua +++ b/modpol_core/modules/template.lua @@ -43,10 +43,11 @@ function module_template:initiate(result) -- call interaction functions here! -- concluding functions: - -- call these wherever process might end; + -- may need to put result in self.data.result - -- if process ends in another function + -- call this when module is successful (not for abort): if result then result() end + -- call this wherever process might end: self.org:delete_process(self.id) end diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 8961a1a..dc7ba88 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -53,7 +53,8 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) return index end -function modpol.orgs:delete_process(id) +function modpol.orgs:delete_process(id) + self:wipe_pending_actions(id) self.processes[id] = 'deleted' end From 6d81bd7bd8ae9ea9bc2414bad0d6e971e7cb8d16 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Thu, 23 Dec 2021 22:25:51 -0700 Subject: [PATCH 04/26] Started working on tokenomics module (but not yet activated) --- modpol_core/modules/tokenomics.lua | 111 +++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 modpol_core/modules/tokenomics.lua diff --git a/modpol_core/modules/tokenomics.lua b/modpol_core/modules/tokenomics.lua new file mode 100644 index 0000000..139a8eb --- /dev/null +++ b/modpol_core/modules/tokenomics.lua @@ -0,0 +1,111 @@ +--- tokenomics +-- @module tokenomics + +local tokenomics = { + name = "Tokenomics", + slug = "tokenomics", + desc = "A simple library for token economics", + hide = true; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +tokenomics.data = { +} + +--- (Required): config for module +-- @field consent Require consent to create? +-- @field token_variables the data that goes into the token +-- @field token_slug A no-spaces slug for the token +-- @field initial_treasury Quantity in org treasury +-- @field negative_spend Boolean: can users spend negative tokens? (for mutual credit) +-- @field balances Table of user balances +tokenomics.config = { + consent = false, + token_slug = "token", + token_variables = { + treasury = 0, + negative_spend = true, + balances = {} + } +} + +--- (Required): initiate function: creates a token in an org +-- set up the token data structure +-- create an org treasury +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function tokenomics:initiate(result) + -- TODO need to create a series of interactions to get the info from users + if self.org.tokens and self.org.tokens[slug] then + modpol.interactions.message( + self.initiator, "Token slug taken, aborting") + self.org:delete_process(self.id) + else + if self.config.consent then + -- TODO consent process, calling create_token + else + self:create_token() + end + end +end + +function tokenomics:create_token() + if not self.org.tokens then self.org.tokens = {} end + self.org.tokens[slug] = self.config.token_variables + modpol.interactions.message_org( + self.initiator, self.org.id, + "Token "..self.config.token_slug.." created") + -- TODO need to add to persistent storage? + if result then result() end + -- call this wherever process might end: + self.org:delete_process(self.id) +end + +----------------------------------------- +-- Utility functions +-- all need to account for the fact that some users may not yet have balances +-- all need to write to persistent data +-- amount can be positive or negative (except transfer) + +-- returns balance +-- if no user, get treasury balance +-- @field org Name (string) or id (num) +-- @field token Slug (string) +-- @field user Name (string) +function tokenomics.balance(org, token, user) + local this_org = modpol.orgs.get_org(org) + if not this_org[token] then return nil, "Token not found" end + if not user then + return this_org[token].treasury + end + if not this_org[user] then + return nil, "User not found" + else + local user_balance = this_org[token].balances[user] + if user_balance then + return user_balance + else + return 0 + end + end +end + +function tokenomics.change_balance(org, token, user, amount) +end + +function tokenomics.transfer(org, token, sender, recipient, amount) +end + +function tokenomics.treasury_transfer(org, token, recipient, amount) +end + +-- creates new tokens in the org treasury +function tokenomics.issue(org, token, amount) +end + +------------------------------------------ + +--- (Required) Add to module table +modpol.modules.tokenomics = tokenomics From c017b800637784b8fd0902cc5b41632c3babcf49 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 25 Dec 2021 18:01:37 -0700 Subject: [PATCH 05/26] Added recording of main process events to persistent storage --- modpol_core/orgs/process.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index dc7ba88..5d60cbe 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -49,11 +49,16 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) self.processes[index] = new_process self.processes[index]:initiate(result) + local msg = "Initiating "..module_slug.. + " process id "..index.." in org "..self.name return index end function modpol.orgs:delete_process(id) + local msg = "Deleting "..self.processes[id].slug.. + " process id "..id.." in org "..self.name + self:record(msg, self.processes[id].slug) self:wipe_pending_actions(id) self.processes[id] = 'deleted' end @@ -97,6 +102,10 @@ function modpol.orgs:interact(process_id, user) local callback = self.pending[user][process_id] if callback then process[callback](process, user) + -- record org data + local msg = "Updating "..self.processes[process_id].slug.. + " process id "..process_id.." in org "..self.name + self:record(msg, self.processes[process_id].slug) end end end From 12a49ab66d3a8f385dded7d764e7a98b35402325 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 25 Dec 2021 18:14:38 -0700 Subject: [PATCH 06/26] Added record() function to tokenomics module functions --- modpol_core/modules/tokenomics.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modpol_core/modules/tokenomics.lua b/modpol_core/modules/tokenomics.lua index 139a8eb..e212bbe 100644 --- a/modpol_core/modules/tokenomics.lua +++ b/modpol_core/modules/tokenomics.lua @@ -93,16 +93,20 @@ function tokenomics.balance(org, token, user) end function tokenomics.change_balance(org, token, user, amount) + self.org:record("Changed token balance",self.slug) end function tokenomics.transfer(org, token, sender, recipient, amount) + -- use change_balance end function tokenomics.treasury_transfer(org, token, recipient, amount) + -- use change_balance end -- creates new tokens in the org treasury function tokenomics.issue(org, token, amount) + self.org:record("Issued tokes to treasury",self.slug) end ------------------------------------------ From 0c59767ef7e9a21b3572c09c87ee314b88782b35 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sun, 26 Dec 2021 00:01:59 -0700 Subject: [PATCH 07/26] Filled out tokenomics functions; still not tested --- modpol_core/modules/remove_org_consent.lua | 2 +- modpol_core/modules/tokenomics.lua | 108 +++++++++++++++++++-- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/modpol_core/modules/remove_org_consent.lua b/modpol_core/modules/remove_org_consent.lua index f9e8513..30891a0 100644 --- a/modpol_core/modules/remove_org_consent.lua +++ b/modpol_core/modules/remove_org_consent.lua @@ -31,7 +31,7 @@ function remove_org_consent:initiate(result) prompt = "Remove org " .. self.org.name .. "?", votes_required = #self.org.members }, - function () + function() self:complete() end ) diff --git a/modpol_core/modules/tokenomics.lua b/modpol_core/modules/tokenomics.lua index e212bbe..6fd6805 100644 --- a/modpol_core/modules/tokenomics.lua +++ b/modpol_core/modules/tokenomics.lua @@ -12,6 +12,7 @@ local tokenomics = { -- Variables that module uses during the course of a process -- Can be blank tokenomics.data = { + result = nil } --- (Required): config for module @@ -38,13 +39,24 @@ tokenomics.config = { -- @function initiate function tokenomics:initiate(result) -- TODO need to create a series of interactions to get the info from users + self.data.result = result if self.org.tokens and self.org.tokens[slug] then modpol.interactions.message( self.initiator, "Token slug taken, aborting") self.org:delete_process(self.id) else if self.config.consent then - -- TODO consent process, calling create_token + self.org:call_module( + "consent", + self.initiator, + { + prompt = "Create token "..self.token_slug.."?", + votes_required = #self.org.members + }, + function() + self:create_token() + end + ) else self:create_token() end @@ -58,7 +70,7 @@ function tokenomics:create_token() self.initiator, self.org.id, "Token "..self.config.token_slug.." created") -- TODO need to add to persistent storage? - if result then result() end + if self.data.result then self.data.result() end -- call this wherever process might end: self.org:delete_process(self.id) end @@ -71,9 +83,9 @@ end -- returns balance -- if no user, get treasury balance --- @field org Name (string) or id (num) --- @field token Slug (string) --- @field user Name (string) +-- @param org Name (string) or id (num) +-- @param token Slug (string) +-- @param user Name (string) function tokenomics.balance(org, token, user) local this_org = modpol.orgs.get_org(org) if not this_org[token] then return nil, "Token not found" end @@ -92,21 +104,99 @@ function tokenomics.balance(org, token, user) end end +-- @param org Org name (string) or id (number) +-- @param token Token slug (string) function tokenomics.change_balance(org, token, user, amount) - self.org:record("Changed token balance",self.slug) + local this_org = modpol.orgs.get_org(org) + if not this_org then + return nil, "Cannot change balance: Org not found" + elseif not this_org.tokens then + return nil, "Cannot change balance: no tokens found" + elseif not this_org.tokens[token] then + return nil, "Cannot change balance: token not found" + elseif not this_org.members[user] then + return nil, "Cannot change balance: user not in org" + elseif not tonumber(amount) then + return nil, "Cannot change balance: invalid amount" + else + local old_balance = this_org.tokens[token].balances[user] + if not old_balance then old_balance = 0 end + local new_balance = old_balance + amount + this_org.tokens[token].balances[user] = new_balance + local msg = "Your balance of token "..token.. + " changed from "..old_balance.." to "..new_balance + modpol.interactions.message(user, msg) + self.org:record( + "Changed token balance for "..user,self.slug) + end end +-- @param amount Positive number function tokenomics.transfer(org, token, sender, recipient, amount) - -- use change_balance + local sender_balance = tokenomics.balance(org, token, sender) + local recipient_balance = tokenomics.balance(org, token, recipient) + if not sender_balance or recipient balance then + return nil, "Transfer failed, user not found" + else + sender_balance = sender_balance - amount + recipient_balance = recipient_balance + amount + local neg_spend = modpol.orgs.get_org(org).tokens[token].negative_spend + if sender_balance < 0 and not neg_spend then + return nil, "Transfer failed, negative spend not allowed" + else + tokenomics.change_balance( + org, token, sender, sender_balance) + tokenomics.change_balance( + org, token, recipient, recipient_balance) + return "Transfer complete" + end + end end +-- @param amount Can be positive or negative, assumes flow from treasury to recipient function tokenomics.treasury_transfer(org, token, recipient, amount) - -- use change_balance + local this_org = modpol.orgs.get_org(org) + if not this_org then + return nil, "Cannot transfer treasury: Org not found" + elseif not this_org.tokens then + return nil, "Cannot transfer treasury: no tokens found" + elseif not this_org.tokens[token] then + return nil, "Cannot transfer treasury: token not found" + elseif not this_org.members[user] then + return nil, "Cannot transfer treasury: user not in org" + elseif not tonumber(amount) then + return nil, "Cannot change balance: invalid amount" + else + local new_treasury = this_org.tokens[token].treasury - amount + local new_recipient_balance = tokenomics.balance(org, token, recipient) + amount + if new_treasury < 0 and not this_org.tokens[token].negative_spend then + return nil, "Transfer failed, negative spend not allowed" + elseif new_recipient_balance < 0 and not this_org.tokens[token].negative_spend then + return nil, "Transfer failed, negative spend not allowed" + else + this_org.tokens[token].treasury = new_treasury + self.org:record("Treasury payment",self.slug) + tokenomics.change_balance(org, token, recipient, amount) + end + end end -- creates new tokens in the org treasury function tokenomics.issue(org, token, amount) - self.org:record("Issued tokes to treasury",self.slug) + local this_org = modpol.orgs.get_org(org) + if not this_org then + return nil, "Cannot transfer treasury: Org not found" + elseif not this_org.tokens then + return nil, "Cannot transfer treasury: no tokens found" + elseif not this_org.tokens[token] then + return nil, "Cannot transfer treasury: token not found" + elseif not tonumber(amount) then + return nil, "Cannot change balance: invalid amount" + else + this_org.tokens[token].treasury = + this_org.tokens[token].treasury + amount + self.org:record("Issued tokes to treasury","tokenomics") + end end ------------------------------------------ From 286d13183957c48b43c0c27f795ec891d00b9383 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sun, 26 Dec 2021 23:03:57 -0700 Subject: [PATCH 08/26] Got first token modules working, and a bunch of bugfixes throughout --- login.lua | 1 + modpol_core/api.lua | 3 + modpol_core/interactions/interactions.lua | 6 ++ modpol_core/modules/change_modules.lua | 2 - modpol_core/modules/create_token.lua | 53 +++++++++++++++ modpol_core/modules/send_token.lua | 77 ++++++++++++++++++++++ modpol_core/modules/tokenomics.lua | 18 +++-- modpol_core/orgs/process.lua | 12 ++-- modpol_core/util/misc.lua | 11 +++- modpol_minetest/api.lua | 5 -- modpol_minetest/chatcommands.lua | 1 + modpol_minetest/modules/priv_to_org.lua | 2 +- modpol_minetest/overrides/interactions.lua | 10 +++ 13 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 modpol_core/modules/create_token.lua create mode 100644 modpol_core/modules/send_token.lua diff --git a/login.lua b/login.lua index e21e497..3c51fe6 100644 --- a/login.lua +++ b/login.lua @@ -1,5 +1,6 @@ dofile("modpol_core/modpol.lua") +modpol.instance.members = {} modpol.orgs.reset() print("Log in as which user?") diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 8c5e4cd..dea3ba3 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -15,6 +15,7 @@ dofile (localdir .. "/interactions/interactions.lua") dofile (localdir .. "/modules/add_child_org_consent.lua") dofile (localdir .. "/modules/change_modules.lua") dofile (localdir .. "/modules/consent.lua") +dofile (localdir .. "/modules/create_token.lua") dofile (localdir .. "/modules/defer_consent.lua") dofile (localdir .. "/modules/join_org_consent.lua") dofile (localdir .. "/modules/leave_org.lua") @@ -25,3 +26,5 @@ dofile (localdir .. "/modules/remove_member_consent.lua") dofile (localdir .. "/modules/remove_org_consent.lua") dofile (localdir .. "/modules/remove_org.lua") dofile (localdir .. "/modules/rename_org_consent.lua") +dofile (localdir .. "/modules/send_token.lua") +dofile (localdir .. "/modules/tokenomics.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 501dc11..05ff57b 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -269,3 +269,9 @@ end -- output: gets question from initiator, asks all org members, broadcasts answers +-- TESTING + +--testing command +function modpol.msg(text) + modpol.interactions.message("TEST MSG: ",text) +end diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index 9e2fe14..58986c7 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -148,12 +148,10 @@ function change_modules:propose_change(type, mod_text) "Consent reached:\nAdding \"" ..mod_text.."\" to org "..self.org.name) elseif type == "remove" then - modpol.msg("Removing!") local i = 0 for k,v in pairs(self.org.modules) do i = i + 1 if v.name == mod_text then - modpol.msg("got it!") self.org.modules[k] = nil end end diff --git a/modpol_core/modules/create_token.lua b/modpol_core/modules/create_token.lua new file mode 100644 index 0000000..6e54139 --- /dev/null +++ b/modpol_core/modules/create_token.lua @@ -0,0 +1,53 @@ +--- create_token +-- @module create_token +-- depends on tokenomics + +local create_token = { + name = "Create a token (consent)", + slug = "create_token", + desc = "With org consent, creates an org token", + hide = false; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +create_token.data = { +} + +create_token.config = { + token_name = "" +} + +--- (Required): initiate function +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function create_token:initiate(result) + modpol.interactions.text_query( + self.initiator, + "Token name (alpha-numeric, no spaces):", + function(input) + self.config.token_name = input + self.org:call_module( + "tokenomics", + self.initiator, + { + consent = true, + token_slug = self.config.token_name + }, + function(input2) + modpol.interactions.org_dashboard( + self.initiator, self.org.name) + if result then result() end + -- call this wherever process might end: + self.org:delete_process(self.id) + end + ) + modpol.interactions.org_dashboard( + self.initiator, self.org.name) + end + ) +end + +--- (Required) Add to module table +modpol.modules.create_token = create_token diff --git a/modpol_core/modules/send_token.lua b/modpol_core/modules/send_token.lua new file mode 100644 index 0000000..84e0019 --- /dev/null +++ b/modpol_core/modules/send_token.lua @@ -0,0 +1,77 @@ +--- send_token +-- @module send_token +-- depends on tokenomics + +local send_token = { + name = "Send tokens", + slug = "send_token", + desc = "Send tokens to another user", + hide = false; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +send_token.data = { +} + +send_token.config = { + token_name = "" +} + +--- (Required): initiate function +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function send_token:initiate(result) + local token_list = {} + if self.org.tokens then + for k,v in pairs(self.org.tokens) do + table.insert(token_list, k) + end + end + if token_list == {} then + modpol.interactions.message( + self.initiator, + "No tokens in org") + modpol.interactions.org_dashboard( + self.initiator, self.org.name) + self.org:delete_process(self.id) + return + else + modpol.interactions.dropdown_query( + self.initiator, + "Which token do you want to send?", + token_list, + function(input_token) + modpol.interactions.dropdown_query( + self.initiator, + "Who do you want to send to?", + modpol.util.copy_table(self.org.members), + function(input_recipient) + modpol.interactions.text_query( + self.initiator, + "How much do you want to give (a number)?", + function(input_amount) + modpol.modules.tokenomics.transfer( + self.org.id, + input_token, + self.initiator, + input_recipient, + input_amount + ) + modpol.interactions.org_dashboard( + self.initiator, self.org.name) + -- close process + if result then result() end + self.org:delete_process(self.id) + end + ) + end + ) + end + ) + end +end + +--- (Required) Add to module table +modpol.modules.send_token = send_token diff --git a/modpol_core/modules/tokenomics.lua b/modpol_core/modules/tokenomics.lua index 6fd6805..e968b29 100644 --- a/modpol_core/modules/tokenomics.lua +++ b/modpol_core/modules/tokenomics.lua @@ -1,5 +1,6 @@ --- tokenomics -- @module tokenomics +-- Depends on consent local tokenomics = { name = "Tokenomics", @@ -50,10 +51,15 @@ function tokenomics:initiate(result) "consent", self.initiator, { - prompt = "Create token "..self.token_slug.."?", + prompt = "Create token ".. + self.config.token_slug.."?", votes_required = #self.org.members }, function() + modpol.interactions.message_org( + self.initiator, self.org.id, + "Consent reached: creating token ".. + self.config.token_slug) self:create_token() end ) @@ -65,11 +71,13 @@ end function tokenomics:create_token() if not self.org.tokens then self.org.tokens = {} end - self.org.tokens[slug] = self.config.token_variables + self.org.tokens[self.config.token_slug] = + self.config.token_variables + self.org:record("Created token "..self.config.token_slug, + self.slug) modpol.interactions.message_org( self.initiator, self.org.id, - "Token "..self.config.token_slug.." created") - -- TODO need to add to persistent storage? + "Created token "..self.config.token_slug) if self.data.result then self.data.result() end -- call this wherever process might end: self.org:delete_process(self.id) @@ -135,7 +143,7 @@ end function tokenomics.transfer(org, token, sender, recipient, amount) local sender_balance = tokenomics.balance(org, token, sender) local recipient_balance = tokenomics.balance(org, token, recipient) - if not sender_balance or recipient balance then + if not sender_balance or recipient_balance then return nil, "Transfer failed, user not found" else sender_balance = sender_balance - amount diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 5d60cbe..cb5ae0a 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -26,7 +26,7 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) local module = modpol.modules[module_slug] -- sets default values for undeclared config variables - if #module.config > 0 then + if modpol.util.num_pairs(module.config) > 0 and config then for k, v in pairs(module.config) do if config[k] == nil then config[k] = v @@ -34,6 +34,7 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) end end + -- setting default params local new_process = { metatable = {__index = module}, @@ -101,11 +102,14 @@ function modpol.orgs:interact(process_id, user) if self.pending[user] then local callback = self.pending[user][process_id] if callback then + -- get data in case callback ends process + local slug = self.processes[process_id].slug + -- run callback process[callback](process, user) -- record org data - local msg = "Updating "..self.processes[process_id].slug.. - " process id "..process_id.." in org "..self.name - self:record(msg, self.processes[process_id].slug) + local msg = "Updating "..slug.. + " process id "..process_id.." in org "..self.name + self:record(msg, slug) end end end diff --git a/modpol_core/util/misc.lua b/modpol_core/util/misc.lua index e6ca585..1707dab 100644 --- a/modpol_core/util/misc.lua +++ b/modpol_core/util/misc.lua @@ -1,11 +1,18 @@ modpol.util = {} --- @function modpol.copy_table +-- @param t table -- Returns a copy of the table inputted function modpol.util.copy_table(t) local t2 = {} - for k,v in pairs(t) do - t2[k] = v + if ipairs(t) then + for i,v in ipairs(t) do + t2[i] = v + end + elseif pairs(t) then + for k,v in pairs(t) do + t2[k] = v + end end return t2 end diff --git a/modpol_minetest/api.lua b/modpol_minetest/api.lua index 21e9a59..184e8c0 100644 --- a/modpol_minetest/api.lua +++ b/modpol_minetest/api.lua @@ -11,11 +11,6 @@ local localdir = minetest.get_modpath("modpol") .. "/modpol_minetest" dofile (localdir .. "/overrides/interactions.lua") dofile (localdir .. "/overrides/users.lua") ---testing command for "singleplayer" -function modpol.msg(text) - modpol.interactions.message("singleplayer",text) -end - -- =================================================================== -- Minetest Chatcommands -- =================================================================== diff --git a/modpol_minetest/chatcommands.lua b/modpol_minetest/chatcommands.lua index 34f204e..182dcf9 100644 --- a/modpol_minetest/chatcommands.lua +++ b/modpol_minetest/chatcommands.lua @@ -34,6 +34,7 @@ regchat( "mptest", { privs = {privs=true}, func = function(user) + modpol.instance.members = modpol.list_users() modpol.orgs.reset() modpol.interactions.dashboard(user) return true, "Reset orgs" diff --git a/modpol_minetest/modules/priv_to_org.lua b/modpol_minetest/modules/priv_to_org.lua index 086fd0e..2a960b2 100644 --- a/modpol_minetest/modules/priv_to_org.lua +++ b/modpol_minetest/modules/priv_to_org.lua @@ -20,7 +20,7 @@ priv_to_org.config = { function priv_to_org:initiate(result) local player_privs = minetest.get_player_privs(self.initiator) -- construct table for display - local player_privs_table = {"View..."} + local player_privs_table = {} for k,v in pairs(player_privs) do if player_privs[k] then table.insert(player_privs_table,k) diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index 3b8d9b9..3444a2d 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -218,6 +218,9 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) function(input) if input == "Yes" then org:call_module(module.slug, pname) + elseif input == "No" then + modpol.interactions.org_dashboard( + pname, org.id) end end) end @@ -389,3 +392,10 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) minetest.close_formspec(pname, formname) end end) + +-- TESTING + +--testing command for "singleplayer" +function modpol.msg(text) + modpol.interactions.message("singleplayer",text) +end From de8ad25b2fd73b99878dd2e09a3a0b91d019f361 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Mon, 27 Dec 2021 22:34:22 -0700 Subject: [PATCH 09/26] Added user_dashboard and message_user in modpol_core, still have to add to modpol_minetest --- modpol_core/interactions/interactions.lua | 121 ++++++++++++++++++--- modpol_minetest/overrides/interactions.lua | 14 ++- 2 files changed, 119 insertions(+), 16 deletions(-) diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 05ff57b..55584b3 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -49,18 +49,40 @@ function modpol.interactions.dashboard(user) print('All users: ' .. table.concat(all_users, ', ')) print() - print('Access which org id?') + print("Commands: (O)rg, (U)ser, Enter to close") + 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 - print("Org id not found.") - end + if sel == "O" or sel == "o" then + print('Access which org id?') + 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 + print("Org id not found") + modpol.interactions.dashboard(user) + end + + elseif sel == "U" or sel == "u" then + print("Access which user?") + sel = io.read() + print() + if modpol.instance:has_member(sel) then + modpol.interactions.user_dashboard( + user, sel, + function() + modpol.interactions.dashboard(user) + end + ) + else + print("User name not found") + modpol.interactions.dashboard(user) + end + end end - + -- Function: modpol.interactions.org_dashboard -- Params: user (string), org_string (string or id) @@ -165,20 +187,91 @@ function modpol.interactions.org_dashboard(user, org_string) end end --- Function: modpol.interactions.policy_dashboard --- input: user (string), org_id (int), policy (string) --- if policy is nil, enables creating a new policy --- output: opens a dashboard for viewing/editing policy details --- TODO +--- Function: modpol.interactions.user_dashboard +-- Displays a dashboard about a particular user +-- @param viewer Name of user viewing the dashboard (string) +-- @param user Name of user being viewed (string) +-- @param completion Optional function to call on Done button +function modpol.interactions.user_dashboard(viewer, user, completion) + local user_orgs = {} + local user_modules = {} + print("\n-=< USER DASHBOARD: "..user.." >=-") + print("User's orgs:") + for id, org in ipairs(modpol.orgs.array) do + if type(org) == "table" then + if org:has_member(user) then + print(org.name) + end + end + end + + print() + print("Commands: (M)essage user, Enter when done") + local sel = io.read() + + if sel == "M" or sel == "m" then + print("Enter your message for "..user..":") + sel = io.read() + print("Sending message") + modpol.interactions.message_user( + viewer, user, sel) + completion() + else + completion() + end +end + + +-- buttons: message, done + +-- INTERACTION PRIMITIVES +-- ====================== -- Function: modpol.interactions.message +-- Produces a brief message to a user -- input: user (string), message (string) -- output: prints message to CLI function modpol.interactions.message(user, message) print(user .. ": " .. message) end +--- Function: modpol.interactions.message_user +-- Sends a message from one user to another +-- @param sender Name of user sending (string) +-- @param recipient Name of user receiving (string) +-- @param message Message to be sent (string) +function modpol.interactions.message_user(sender, recipient, message) + modpol.interactions.message( + recipient, + message.." [from "..sender.."]") +end + +--- Function: modpol.interactions.display +-- Displays complex data to a user +-- @param user Name of target user (string) +-- @param message Content of message (string or table of strings) +-- @param done Optional function for what happens when user is done +function modpol.interactions.display(user, message, completion) + local output = "" + if type(message) == table then + output = table.concat(message,"\n") + elseif type(message) == string then + output = message + elseif type(message) == number then + output = message + else + return nil, "Error: message not typed for display" + end + print(message) + print("\nEnter to continue") + io.read() + if completion then completion() else + modpol.intereactions.dashboard(user) + end +end + + -- Function: modpol.interactions.text_query -- input: User (string), Query (string), func (function) -- func input: user input (string) diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index 3444a2d..c75d3e2 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -257,10 +257,11 @@ function modpol.interactions.policy_dashboard( end --- INTERACTION FUNCTIONS --- ===================== +-- INTERACTION PRIMITIVES +-- ====================== -- Function: modpol.interactions.message +-- Produces a brief message to a user -- input: user (string), message (string) -- output: displays message to specified user function modpol.interactions.message(user, message) @@ -269,6 +270,15 @@ function modpol.interactions.message(user, message) end end + +--- Function: modpol.interactions.display +-- Displays complex data to a user +-- @param user Name of target user (string) +-- @param message Content of message (string or table of strings) +-- @param done Optional function for what happens when user is done + + + -- Function: modpol.interactions.text_query -- Overrides function at modpol/interactions.lua -- input: user (string), query (string), func (function) From 18a29d674cb0c65be9be2fa3c4ae2032d1ba698e Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Mon, 27 Dec 2021 23:13:10 -0700 Subject: [PATCH 10/26] Added user_dashboard and message_user to modpol_minetest --- modpol_core/interactions/interactions.lua | 14 ++- modpol_minetest/overrides/interactions.lua | 102 ++++++++++++++++++--- 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 55584b3..f8447fc 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -211,11 +211,8 @@ function modpol.interactions.user_dashboard(viewer, user, completion) local sel = io.read() if sel == "M" or sel == "m" then - print("Enter your message for "..user..":") - sel = io.read() - print("Sending message") modpol.interactions.message_user( - viewer, user, sel) + viewer, user) completion() else completion() @@ -237,14 +234,15 @@ function modpol.interactions.message(user, message) end --- Function: modpol.interactions.message_user --- Sends a message from one user to another +-- Gets and sends a message from one user to another -- @param sender Name of user sending (string) -- @param recipient Name of user receiving (string) --- @param message Message to be sent (string) -function modpol.interactions.message_user(sender, recipient, message) +function modpol.interactions.message_user(sender, recipient) + print("Enter your message for "..recipient..":") + local sel = io.read() modpol.interactions.message( recipient, - message.." [from "..sender.."]") + sel.." [from "..sender.."]") end --- Function: modpol.interactions.display diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index c75d3e2..8de043d 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -89,7 +89,15 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) minetest.close_formspec(pname, formname) elseif fields.refresh then modpol.interactions.dashboard(pname) - -- Put all dropdowns at the end + -- Put all dropdowns at the end + elseif fields.all_users then + modpol.interactions.user_dashboard( + pname, + fields.all_users, + function() + modpol.interactions.dashboard(pname) + end + ) elseif fields.all_orgs or fields.user_orgs or fields.pending then local org_name = fields.all_orgs or fields.user_orgs or fields.pending modpol.interactions.org_dashboard(pname, org_name) @@ -169,7 +177,7 @@ function modpol.interactions.org_dashboard(user, org_string) minetest.formspec_escape(org.name)..membership_toggle(org.name).."]", "label[0.5,1;Parent: "..parent..membership_toggle(parent).."]", "label[0.5,2;Members:]", - "dropdown[2,1.5;7,0.8;user_orgs;"..formspec_list(org.members)..";;]", + "dropdown[2,1.5;7,0.8;members;"..formspec_list(org.members)..";;]", "label[0.5,3;Child orgs:]", "dropdown[2,2.5;7,0.8;children;"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", @@ -198,10 +206,19 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) elseif fields.back then modpol.interactions.dashboard(pname) elseif fields.refresh then - modpol.interactions.org_dashboard(pname,org.name) + modpol.interactions.org_dashboard(pname, org.name) -- Put all dropdowns at the end - -- Receiving modules + -- Receiving modules + elseif fields.members then + modpol.interactions.user_dashboard( + pname, + fields.members, + function() + modpol.interactions.org_dashboard( + pname, org.name) + end + ) elseif fields.modules and fields.modules ~= "View..." then local module = nil @@ -244,17 +261,60 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) end end) +--- Function: modpol.interactions.user_dashboard +-- Displays a dashboard about a particular user +-- @param viewer Name of user viewing the dashboard (string) +-- @param user Name of user being viewed (string) +-- @param completion Optional function to call on Done button +function modpol.interactions.user_dashboard(viewer, user, completion) + local user_orgs = modpol.orgs.user_orgs(user) + table.insert(user_orgs,1,"View...") --- Function: modpol.interactions.policy_dashboard --- input: user (string), org_id (int), policy (string) --- output: opens a dashboard for viewing/editing policy details --- TODO -function modpol.interactions.policy_dashboard( - user, org_id, policy) - modpol.interactions.message( - user, - "Not yet implemented: " .. policy) + -- set player context + local user_context = {} + user_context["viewer"] = viewer + user_context["user"] = user + user_context["completion"] = completion + _contexts[viewer] = user_context + -- set up formspec + local formspec = { + "formspec_version[4]", + "size[10,8]", + "label[0.5,0.5;User: "..user.."]", + "label[0.5,2;User's orgs:]", + "dropdown[2,1.5;7,0.8;user_orgs;"..formspec_list(user_orgs)..";;]", + "button[0.5,7;1.5,0.8;message;Message]", + "button_exit[8.5,7;1,0.8;close;Close]", + } + local formspec_string = table.concat(formspec, "") + -- present to player + minetest.show_formspec(viewer, "modpol:user_dashboard", formspec_string) end +-- receive input +minetest.register_on_player_receive_fields(function (player, formname, fields) + if formname == "modpol:user_dashboard" then + local contexts = _contexts[player:get_player_name()] + -- check fields + if nil then + elseif fields.message then + modpol.interactions.message_user( + contexts.viewer, contexts.user + ) + elseif fields.back then + if contexts.completion then + completion() + else + modpol.interactions.dashboard( + contexts.viewer) + end + -- dropdown fields + elseif fields.user_orgs + and fields.user_orgs ~= "View..." then + modpol.interactions.org_dashboard( + contexts.viewer, fields.user_orgs) + end + end +end) -- INTERACTION PRIMITIVES @@ -271,6 +331,22 @@ function modpol.interactions.message(user, message) end +--- Function: modpol.interactions.message_user +-- Gets and sends a message from one user to another +-- @param sender Name of user sending (string) +-- @param recipient Name of user receiving (string) +function modpol.interactions.message_user(sender, recipient) + modpol.interactions.text_query( + sender, + "Message for "..recipient..":", + function(input) + modpol.interactions.message( + recipient, + input.." [from "..sender.."]") + end + ) +end + --- Function: modpol.interactions.display -- Displays complex data to a user -- @param user Name of target user (string) From 1b0335c069d5b54bb43d5111a3a4af046bc5e88e Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Tue, 28 Dec 2021 14:39:34 -0700 Subject: [PATCH 11/26] Bugfix on copy_table and added refresh command to CLI --- README.md | 4 ++-- login.lua | 3 --- modpol_core/interactions/interactions.lua | 16 ++++++++++++++-- modpol_core/util/misc.lua | 6 +----- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 28152e1..16f96c2 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ The command-line version is in the `modpol` subdirectory. To run the program on $ lua[jit] login.lua ``` -You can also interact with the interpreter by starting it this way: +Alternatively, to test arbitrary functions in the interpreter outside of the interactive dashboards, load Modpol with: ``` $ lua[jit] -> dofile("login.lua") +> dofile("modpol_core/modpol.lua") ``` In the interpreter, for a list of global functions and tables, use `modpol.menu()`. diff --git a/login.lua b/login.lua index 3c51fe6..a29a58c 100644 --- a/login.lua +++ b/login.lua @@ -1,8 +1,5 @@ dofile("modpol_core/modpol.lua") -modpol.instance.members = {} -modpol.orgs.reset() - print("Log in as which user?") local username = io.read() diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index f8447fc..451d7fd 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -1,6 +1,6 @@ -- INTERACTIONS.LUA (CLI) --- User interaction functions for Modular Politics +-- User interaction functions for Modpol -- Called by modpol.lua modpol.interactions = {} @@ -49,7 +49,7 @@ function modpol.interactions.dashboard(user) print('All users: ' .. table.concat(all_users, ', ')) print() - print("Commands: (O)rg, (U)ser, Enter to close") + print("Commands: (O)rg, (U)ser, (R)eset, Enter to close") local sel = io.read() @@ -76,10 +76,22 @@ function modpol.interactions.dashboard(user) modpol.interactions.dashboard(user) end ) + + elseif sel == "" then + return + else print("User name not found") modpol.interactions.dashboard(user) end + elseif sel == "R" or sel == "r" then + modpol.instance.members = {} + modpol.orgs.reset() + print("Orgs and users reset") + modpol.interactions.dashboard(user) + else + print("Invalid input, try again") + modpol.interactions.dashboard(user) end end diff --git a/modpol_core/util/misc.lua b/modpol_core/util/misc.lua index 1707dab..aa4ba47 100644 --- a/modpol_core/util/misc.lua +++ b/modpol_core/util/misc.lua @@ -5,11 +5,7 @@ modpol.util = {} -- Returns a copy of the table inputted function modpol.util.copy_table(t) local t2 = {} - if ipairs(t) then - for i,v in ipairs(t) do - t2[i] = v - end - elseif pairs(t) then + if pairs(t) then for k,v in pairs(t) do t2[k] = v end From 6558b7a026fb839c0cc654a0903466c587e0a607 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Tue, 28 Dec 2021 16:51:12 -0700 Subject: [PATCH 12/26] Created remove_process module---mostly works, but there are still some issues with processes and pending actions not being removed properly --- modpol_core/api.lua | 1 + modpol_core/interactions/interactions.lua | 13 +-- modpol_core/modules/consent.lua | 6 ++ modpol_core/modules/remove_process.lua | 113 ++++++++++++++++++++++ modpol_core/orgs/process.lua | 15 +-- 5 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 modpol_core/modules/remove_process.lua diff --git a/modpol_core/api.lua b/modpol_core/api.lua index dea3ba3..6af2d5a 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -25,6 +25,7 @@ dofile (localdir .. "/modules/remove_child_consent.lua") dofile (localdir .. "/modules/remove_member_consent.lua") dofile (localdir .. "/modules/remove_org_consent.lua") dofile (localdir .. "/modules/remove_org.lua") +dofile (localdir .. "/modules/remove_process.lua") dofile (localdir .. "/modules/rename_org_consent.lua") dofile (localdir .. "/modules/send_token.lua") dofile (localdir .. "/modules/tokenomics.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 451d7fd..498dda4 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -49,7 +49,7 @@ function modpol.interactions.dashboard(user) print('All users: ' .. table.concat(all_users, ', ')) print() - print("Commands: (O)rg, (U)ser, (R)eset, Enter to close") + print("Commands: (O)rg, (U)ser, (R)eset, (Q)uit") local sel = io.read() @@ -75,20 +75,21 @@ function modpol.interactions.dashboard(user) function() modpol.interactions.dashboard(user) end - ) - - elseif sel == "" then - return - + ) else print("User name not found") modpol.interactions.dashboard(user) end + elseif sel == "R" or sel == "r" then modpol.instance.members = {} modpol.orgs.reset() print("Orgs and users reset") modpol.interactions.dashboard(user) + + elseif sel == "Q" or "q" then + return + else print("Invalid input, try again") modpol.interactions.dashboard(user) diff --git a/modpol_core/modules/consent.lua b/modpol_core/modules/consent.lua index c46806e..8e7f7dc 100644 --- a/modpol_core/modules/consent.lua +++ b/modpol_core/modules/consent.lua @@ -41,6 +41,12 @@ function consent:callback(member) if resp == "Yes" then self.data.votes = self.data.votes + 1 end + modpol.interactions.message_org( + "consent", self.org.id, + member.." decided "..resp.." on: ".. + self.config.prompt.." ("..self.data.votes.. + "/"..self.config.votes_required..")" + ) if self.data.votes >= self.config.votes_required then if self.data.result then self.data.result() end diff --git a/modpol_core/modules/remove_process.lua b/modpol_core/modules/remove_process.lua new file mode 100644 index 0000000..d382284 --- /dev/null +++ b/modpol_core/modules/remove_process.lua @@ -0,0 +1,113 @@ +--- remove_process +-- @module remove_process + +local remove_process = { + name = "Remove process", + slug = "remove_process", + desc = "User can remove own processes, consent required for those of others", + hide = false; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +remove_process.data = { +} + +remove_process.config = { +} + +--- (Required): initiate function +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function remove_process:initiate(result) + -- prepare process options + local available_processes = {} + for k,process in pairs(self.org.processes) do + if process ~= "deleted" then + available_processes[process.id] = modpol.util.copy_table(process) + end + end + local process_list = {} + local process_count = 0 + for k,v in pairs(available_processes) do + local mine = "" + if v.initiator == self.initiator then mine = "*" end + table.insert(process_list,"["..v.id.."] "..v.slug..mine) + process_count = process_count + 1 + end + -- abort if no processes to remove + if process_count == 0 then + modpol.interactions.message( + self.initiator, "Org has no modules") + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if result then result() end + self.org:delete_process(self.id) + return + end + table.sort(process_list) + -- now ask which to remove + modpol.interactions.dropdown_query( + self.initiator, "Choose a process to remove (* marks yours, no consent required):", + process_list, + function(process_choice) + -- confirm choice + local process_id = tonumber( + string.match(process_choice, "%d+")) + local process_mine = string.match(process_choice, + "%*") + modpol.interactions.binary_poll_user( + self.initiator, + "Confirm: Remove process \"".. + process_choice .. "\"?", + function(input) + if input == "Yes" then + if process_mine then + self.org:delete_process(process_id) + modpol.interactions.message( + self.initiator, + "Removed process: "..process_choice) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if result then result() end + self.org:delete_process(self.id) + else + self.org:call_module( + "consent", + self.initiator, + { + prompt = "Approve removal of process "..process_choice.."?", + votes_required = #self.org.members + }, + function(input) + modpol.interactions.message_org( + self.initiator, + self.org.id, + "Removing process: ".. + process_choice) + self.org:delete_process(process_id) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if result then result() end + self.org:delete_process(self.id) + end + ) + end + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + else + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if result then result() end + self.org:delete_process(self.id) + end + end + ) + end + ) +end + + +--- (Required) Add to module table +modpol.modules.remove_process = remove_process diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index cb5ae0a..9ebb03b 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -57,11 +57,14 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) end function modpol.orgs:delete_process(id) - local msg = "Deleting "..self.processes[id].slug.. - " process id "..id.." in org "..self.name - self:record(msg, self.processes[id].slug) - self:wipe_pending_actions(id) - self.processes[id] = 'deleted' + if self.processes[id] + and self.processes[id] ~= "deleted" then + local msg = "Deleting "..self.processes[id].slug.. + " process id "..id.." in org "..self.name + self:record(msg, self.processes[id].slug) + self:wipe_pending_actions(id) + self.processes[id] = 'deleted' + end end function modpol.orgs:add_pending_action(process_id, user, callback) @@ -101,7 +104,7 @@ 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 + if callback and process ~= "deleted" then -- get data in case callback ends process local slug = self.processes[process_id].slug -- run callback From 5a2e6277e5177547b655e3aadc99b48f572fef22 Mon Sep 17 00:00:00 2001 From: Luke Miller Date: Wed, 29 Dec 2021 21:10:00 -0500 Subject: [PATCH 13/26] added a module version of call_module which automatically handles parent ids in the backend --- modpol_core/orgs/process.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 9ebb03b..5f9d170 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -1,6 +1,6 @@ --- Process functions for orgs -function modpol.orgs:call_module(module_slug, initiator, config, result) +function modpol.orgs:call_module(module_slug, initiator, config, result, parent_id) if not modpol.modules[module_slug] then modpol.ocutil.log('Error in ' .. self.name .. ':call_module -> module "' .. module_slug .. '" not found') return @@ -34,18 +34,23 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) end end - -- setting default params local new_process = { metatable = {__index = module}, initiator = initiator, org = self, id = index, + parent_id = parent_id, config = config, data = module.data, slug = module_slug } + -- call module wrapper for modules, passes its id to child process when called + function new_process:call_module(module_slug, initiator, config, result) + self.org:call_module(module_slug, initiator, config, result, self.id) + end + setmetatable(new_process, new_process.metatable) self.processes[index] = new_process From 7ef06263577d3ee21edf67db35c32ef513a66fa1 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 29 Dec 2021 22:07:53 -0700 Subject: [PATCH 14/26] Added modpol.interactions.display and display_processes module, as well as a bunch of bugfixes --- modpol_core/api.lua | 1 + modpol_core/interactions/interactions.lua | 19 +++-- modpol_core/modules/display_processes.lua | 61 ++++++++++++++ modpol_core/modules/template.lua | 4 +- modpol_minetest/overrides/interactions.lua | 98 +++++++++++++++------- 5 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 modpol_core/modules/display_processes.lua diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 6af2d5a..05755ca 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -17,6 +17,7 @@ dofile (localdir .. "/modules/change_modules.lua") dofile (localdir .. "/modules/consent.lua") dofile (localdir .. "/modules/create_token.lua") dofile (localdir .. "/modules/defer_consent.lua") +dofile (localdir .. "/modules/display_processes.lua") dofile (localdir .. "/modules/join_org_consent.lua") dofile (localdir .. "/modules/leave_org.lua") dofile (localdir .. "/modules/message_org.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 498dda4..b047c10 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -168,6 +168,7 @@ function modpol.interactions.org_dashboard(user, org_string) org:call_module(module_sel, user) else print("Error: Module not found.") + modpol.interactions.org_dashboard(user, org.id) end elseif sel == 'p' or sel == 'P' then @@ -261,18 +262,26 @@ end --- Function: modpol.interactions.display -- Displays complex data to a user -- @param user Name of target user (string) +-- @param title Title of display (string) -- @param message Content of message (string or table of strings) -- @param done Optional function for what happens when user is done -function modpol.interactions.display(user, message, completion) +function modpol.interactions.display(user, title, message, completion) local output = "" - if type(message) == table then + output = "\n-=< "..title.." >=-\n\n" + if type(message) == "table" then output = table.concat(message,"\n") - elseif type(message) == string then + elseif type(message) == "string" then output = message - elseif type(message) == number then + elseif type(message) == "number" then output = message else - return nil, "Error: message not typed for display" + modpol.interactions.message( + self.initiator, "Error: message not typed for display") + modpol.interactions.message( + self.initiator, "Error: input not typed for display") + if completion then completion() else + modpol.intereactions.dashboard(user) + end end print(message) print("\nEnter to continue") diff --git a/modpol_core/modules/display_processes.lua b/modpol_core/modules/display_processes.lua new file mode 100644 index 0000000..b1b704f --- /dev/null +++ b/modpol_core/modules/display_processes.lua @@ -0,0 +1,61 @@ +--- display_processes +-- @module display_processes + +local display_processes = { + name = "Display processes", + slug = "display_processes", + desc = "Presents a detailed list of org processes", + hide = false; +} + +--- (Required) Data for module +-- Variables that module uses during the course of a process +-- Can be blank +display_processes.data = { +} + +display_processes.config = { +} + +--- (Required): initiate function +-- @param result (optional) Callback if this module is embedded in other modules +-- @function initiate +function display_processes:initiate(result) + local display_table = {} + for k,v in pairs(self.org.processes) do + if v ~= "deleted" then + local input = v.id..": "..v.slug + table.insert(display_table, input) + input = "Org: "..v.org.name.. + ", initiator: "..v.initiator + table.insert(display_table, input) + if v.config + and modpol.util.num_pairs(v.config) > 0 then + table.insert(display_table, "Policies:") + for k2,v2 in pairs(v.config) do + input = k2..": "..v2 + table.insert(display_table, input) + end + end + table.insert(display_table, "\n") + end + end + local output = table.concat(display_table,"\n") + if #display_table == 0 then + output = "No processes found" + end + modpol.interactions.display( + self.initiator, + "Processes in org "..self.org.name, + output, + function() + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if result then result() end + self.org:delete_process(self.id) + end + ) +end + +--- (Required) Add to module table +modpol.modules.display_processes = display_processes diff --git a/modpol_core/modules/template.lua b/modpol_core/modules/template.lua index 1ca63e8..360af59 100644 --- a/modpol_core/modules/template.lua +++ b/modpol_core/modules/template.lua @@ -43,10 +43,12 @@ function module_template:initiate(result) -- call interaction functions here! -- concluding functions: - + -- first, where appropriate, return users to dashboards. + -- second, result: -- may need to put result in self.data.result -- call this when module is successful (not for abort): if result then result() end + -- third, delete the process -- call this wherever process might end: self.org:delete_process(self.id) end diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index 8de043d..c1e92cd 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -49,7 +49,6 @@ function modpol.interactions.dashboard(user) local user_orgs = modpol.orgs.user_orgs(user) local all_users = modpol.instance:list_members() -- pending list - local user_pending = {"View..."} local user_pending_count = 0 for k,v in ipairs(modpol.orgs.array) do if v.pending and v.pending[user] then @@ -64,15 +63,15 @@ function modpol.interactions.dashboard(user) local formspec = { "formspec_version[4]", "size[10,8]", - "label[0.5,0.5;M O D P O L]", + "hypertext[0.5,0.5;9,1;title;Org dashboard]", "label[0.5,2;All orgs:]", - "dropdown[2,1.5;7,0.8;all_orgs;"..formspec_list(all_orgs)..";;]", + "dropdown[2,1.5;7,0.8;all_orgs;View...,"..formspec_list(all_orgs)..";;]", "label[0.5,3;Your orgs:]", - "dropdown[2,2.5;7,0.8;user_orgs;"..formspec_list(user_orgs)..";;]", + "dropdown[2,2.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "label[0.5,4;All users:]", - "dropdown[2,3.5;7,0.8;all_users;"..formspec_list(all_users)..";;]", + "dropdown[2,3.5;7,0.8;all_users;View...,"..formspec_list(all_users)..";;]", "label[0.5,5;Pending ("..user_pending_count.."):]", - "dropdown[2,4.5;7,0.8;pending;"..formspec_list(user_pending)..";;]", + "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(user_pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -90,7 +89,8 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) elseif fields.refresh then modpol.interactions.dashboard(pname) -- Put all dropdowns at the end - elseif fields.all_users then + elseif fields.all_users + and fields.all_users ~= "View..." then modpol.interactions.user_dashboard( pname, fields.all_users, @@ -100,7 +100,10 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) ) elseif fields.all_orgs or fields.user_orgs or fields.pending then local org_name = fields.all_orgs or fields.user_orgs or fields.pending - modpol.interactions.org_dashboard(pname, org_name) + if org_name ~= "View..." then + modpol.interactions.org_dashboard( + pname, org_name) + end end end end) @@ -129,6 +132,9 @@ function modpol.interactions.org_dashboard(user, org_string) if parent then parent = parent.name else parent = "none" end + -- prepare members menu + local members = org.members + -- prepare children menu local children = {} for k,v in ipairs(org.children) do @@ -136,7 +142,6 @@ function modpol.interactions.org_dashboard(user, org_string) table.insert(children, this_child.name) end table.sort(children) - table.insert(children,1,"View...") -- prepare modules menu local modules = {} @@ -149,7 +154,6 @@ function modpol.interactions.org_dashboard(user, org_string) end end table.sort(modules) - table.insert(modules,1,"View...") -- prepare pending menu local pending = {} @@ -163,7 +167,6 @@ function modpol.interactions.org_dashboard(user, org_string) end end table.sort(pending) - table.insert(pending,1,"View...") -- set player context local user_context = {} @@ -173,17 +176,17 @@ function modpol.interactions.org_dashboard(user, org_string) local formspec = { "formspec_version[4]", "size[10,8]", - "label[0.5,0.5;Org: ".. - minetest.formspec_escape(org.name)..membership_toggle(org.name).."]", - "label[0.5,1;Parent: "..parent..membership_toggle(parent).."]", + "hypertext[0.5,0.5;9,1;title;Org: ".. + minetest.formspec_escape(org.name)..""..membership_toggle(org.name).."]", + "label[0.5,1.25;Parent: "..parent..membership_toggle(parent).."]", "label[0.5,2;Members:]", - "dropdown[2,1.5;7,0.8;members;"..formspec_list(org.members)..";;]", + "dropdown[2,1.5;7,0.8;members;View...,"..formspec_list(members)..";;]", "label[0.5,3;Child orgs:]", - "dropdown[2,2.5;7,0.8;children;"..formspec_list(children)..";;]", + "dropdown[2,2.5;7,0.8;children;View...,"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", - "dropdown[2,3.5;7,0.8;modules;"..formspec_list(modules)..";;]", + "dropdown[2,3.5;7,0.8;modules;View...,"..formspec_list(modules)..";;]", "label[0.5,5;Pending ("..num_pending.."):]", - "dropdown[2,4.5;7,0.8;pending;"..formspec_list(pending)..";;]", + "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button[8.5,7;1,0.8;back;Back]", } @@ -210,7 +213,8 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) -- Put all dropdowns at the end -- Receiving modules - elseif fields.members then + elseif fields.members + and fields.members ~= "View..." then modpol.interactions.user_dashboard( pname, fields.members, @@ -268,7 +272,6 @@ end) -- @param completion Optional function to call on Done button function modpol.interactions.user_dashboard(viewer, user, completion) local user_orgs = modpol.orgs.user_orgs(user) - table.insert(user_orgs,1,"View...") -- set player context local user_context = {} @@ -280,9 +283,9 @@ function modpol.interactions.user_dashboard(viewer, user, completion) local formspec = { "formspec_version[4]", "size[10,8]", - "label[0.5,0.5;User: "..user.."]", + "hypertext[0.5,0.5;9,1;title;User: "..user.."]", "label[0.5,2;User's orgs:]", - "dropdown[2,1.5;7,0.8;user_orgs;"..formspec_list(user_orgs)..";;]", + "dropdown[2,1.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "button[0.5,7;1.5,0.8;message;Message]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -350,10 +353,51 @@ end --- Function: modpol.interactions.display -- Displays complex data to a user -- @param user Name of target user (string) +-- @param title Title of display (string) -- @param message Content of message (string or table of strings) --- @param done Optional function for what happens when user is done - - +-- @param completion Optional function for what happens when user is done +function modpol.interactions.display( + user, title, message, completion) + -- set up contexts + _contexts[user]["completion"] = completion + -- set up output + local output = "" + if type(message) == "table" then + output = table.concat(message,"\n") + elseif type(message) == "string" then + output = message + elseif type(message) == "number" then + output = message + else + modpol.interactions.message( + self.initiator, "Error: message not typed for display") + if completion then completion() else + modpol.intereactions.dashboard(user) + end + return + end + -- set up formspec + local formspec = { + "formspec_version[4]", + "size[10,8]", + "label[0.5,0.5;"..title.."]", + "hypertext[0.5,1;9,5.5;display;"..output.."]", + "button_exit[8.5,7;1,0.8;done;Done]", + } + local formspec_string = table.concat(formspec, "") + -- present to player + minetest.show_formspec(user, "modpol:display", formspec_string) +end +-- receive fields +minetest.register_on_player_receive_fields(function (player, formname, fields) + local pname = player:get_player_name() + if formname == "modpol:display" then + if fields.done and _contexts[pname].completion then + minetest.close_formspec(pname, formname) + _contexts[pname].completion() + end + end +end) -- Function: modpol.interactions.text_query -- Overrides function at modpol/interactions.lua @@ -399,14 +443,12 @@ end) -- func input: choice (string) -- output: calls func on user choice function modpol.interactions.dropdown_query(user, label, options, func) - -- Add "View..." to the top of the list - table.insert(options,1,"View...") -- set up formspec local formspec = { "formspec_version[4]", "size[10,4]", "label[0.5,1;"..minetest.formspec_escape(label).."]", - "dropdown[0.5,1.25;9,0.8;input;"..formspec_list(options)..";;]", + "dropdown[0.5,1.25;9,0.8;input;View...,"..formspec_list(options)..";;]", "button[0.5,2.5;1,0.8;cancel;Cancel]", } local formspec_string = table.concat(formspec, "") From 6c9353d3899bb5dc641a2458864604152b153a0d Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 29 Dec 2021 22:10:57 -0700 Subject: [PATCH 15/26] Last commit also changed titles on Minetest dashboards to much more flexible [hypertext] --- modpol_minetest/overrides/interactions.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index c1e92cd..45fb7e0 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -103,7 +103,7 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) if org_name ~= "View..." then modpol.interactions.org_dashboard( pname, org_name) - end + end end end end) @@ -126,7 +126,7 @@ function modpol.interactions.org_dashboard(user, org_string) end return "" end - + -- identify parent local parent = modpol.orgs.get_org(org.parent) if parent then parent = parent.name @@ -134,7 +134,7 @@ function modpol.interactions.org_dashboard(user, org_string) -- prepare members menu local members = org.members - + -- prepare children menu local children = {} for k,v in ipairs(org.children) do @@ -210,7 +210,7 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) modpol.interactions.dashboard(pname) elseif fields.refresh then modpol.interactions.org_dashboard(pname, org.name) - + -- Put all dropdowns at the end -- Receiving modules elseif fields.members @@ -245,7 +245,7 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) end end) end - + -- Receiving pending elseif fields.pending and fields.pending ~= "View..." then @@ -376,7 +376,7 @@ function modpol.interactions.display( end return end - -- set up formspec + -- set up formspec local formspec = { "formspec_version[4]", "size[10,8]", @@ -409,7 +409,7 @@ function modpol.interactions.text_query(user, query, func) "formspec_version[4]", "size[10,4]", "label[0.5,1;", minetest.formspec_escape(query), "]", - "field[0.5,1.25;9,0.8;input;;]", + "field[0.5,1.25;9,0.8;input;;]", "button[0.5,2.5;1,0.8;yes;OK]", } local formspec_string = table.concat(formspec, "") @@ -461,7 +461,7 @@ end -- receive fields minetest.register_on_player_receive_fields(function (player, formname, fields) if formname == "modpol:dropdown_query" then - local pname = player:get_player_name() + local pname = player:get_player_name() if fields.cancel then minetest.close_formspec(pname, formname) elseif fields.input == "View..." then From 1a6f27f2f632f96194dc0af16f1ee728c0ff2cc8 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 29 Dec 2021 22:25:28 -0700 Subject: [PATCH 16/26] More bugfixes on displays and interactions, mainly fixing damage done two commits ago --- modpol_core/modules/display_processes.lua | 13 +++++++++++-- modpol_minetest/overrides/interactions.lua | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/modpol_core/modules/display_processes.lua b/modpol_core/modules/display_processes.lua index b1b704f..5e4fff2 100644 --- a/modpol_core/modules/display_processes.lua +++ b/modpol_core/modules/display_processes.lua @@ -29,11 +29,20 @@ function display_processes:initiate(result) input = "Org: "..v.org.name.. ", initiator: "..v.initiator table.insert(display_table, input) - if v.config + if v.config and modpol.util.num_pairs(v.config) > 0 then table.insert(display_table, "Policies:") for k2,v2 in pairs(v.config) do - input = k2..": "..v2 + local v2_string = "" + if type(v2) ~= "string" + and type(v2) ~= "table" then + v2_string = tostring(v2) + elseif type(v2) == "table" then + v2_string = table.concat(v2) + else + v2_string = "Could not render" + end + input = k2..": "..v2_string table.insert(display_table, input) end end diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index 45fb7e0..e39ac6a 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -50,6 +50,7 @@ function modpol.interactions.dashboard(user) local all_users = modpol.instance:list_members() -- pending list local user_pending_count = 0 + local user_pending = {} for k,v in ipairs(modpol.orgs.array) do if v.pending and v.pending[user] then if modpol.util.num_pairs(v.pending[user]) ~= 0 then From 62d4c0518f65b75caf69b2730621b8ccb84cec1b Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Thu, 30 Dec 2021 11:38:43 -0700 Subject: [PATCH 17/26] Edits to README for readability --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 16f96c2..859ed5d 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,25 @@ # Modpol for Minetest -Modpol, short for "modular politics," is an extension that enables diverse governance processes on multi-user platforms. It offers a library that enables users to create or adapt their own modules that add specific governance functionalities. +Modpol, short for "modular politics," enables diverse governance processes on multi-user platforms. It offers a library with which users can choose, modify, and create modules that add specific governance functionalities. -This implementation is a mod for [Minetest](https://minetest.net), a free/open-source voxel game. It is designed to be easily adapted to other multi-user platforms that also employ Lua as an extension language. +This implementation is a mod for [Minetest](https://minetest.net), a free/open-source voxel game. It is designed to be adapted to other multi-user platforms that also employ Lua as an extension language. ## How to use it -Modpol is built around groups called *orgs*. At the base is an org with all users in it, called `Root` by default. +Modpol is built around groups called *orgs*. At the base is an org with all users in it, called `Root` by default. *Modules* enable people to do things within orgs, such as decide on membership, grant powers to the org, and much more. To get started in Minetest: -*Modules* enable people to do things within orgs, such as decide on membership, grant powers to the org, and much more. Modules can be added and modified by users to meet their needs. Modules can also be nested in each other, so one module can rely on another module to accomplish a process. Within an org, choose the module that you want to use: +* Type the command `/mp` +* Select the org `Root` +* Choose one of its modules to make new orgs and craft their behavior ![](lib/module_list.png) -Modules might simply carry out actions in the game, or they might require a group decision to do so. They might also change the modules available to users of a given org. There are currently two ways of doing this: +Modules can be nested in each other, so one module can rely on another module to accomplish a process. Users might use a module to unilaterally carry out actions in the game, or the module might require a group decision to do so. Users can also change the modules available to users of a given org. There are currently two ways of doing this: -* Remove modules from the list of modules loaded in `modpol_core/api.lua` and `modpol_minetest/api.lua`. This will make those modules no longer available to any user. -* Remove modules for a given org from within the program using the `Change modules` module. The removed modules can be re-added in any org by using `Change modules` again. +* Admins can remove modules from the list of modules loaded in `modpol_core/api.lua` and `modpol_minetest/api.lua`. This will make those modules no longer available to any user. +* Players can change the modules available in a given org from within the program using the `Change modules` module. Removed modules can be re-added in any org by using `Change modules` again. -The point is that Modpol should give you the ability to do whatever kind of politics you want with your modules. If there is something you would like to do that is not available, develop a module for it (or ask us for help!). +Modpol should give you the ability to do whatever kind of politics you want with your modules. If there is something you would like to do that is not available, [develop a module for it](https://gitlab.com/medlabboulder/modpol/-/wikis/Module-Writing-Guide) (or ask us for help!). ## Installation in Minetest @@ -39,7 +41,7 @@ The command-line version is in the `modpol` subdirectory. To run the program on $ lua[jit] login.lua ``` -Alternatively, to test arbitrary functions in the interpreter outside of the interactive dashboards, load Modpol with: +Alternatively, to test arbitrary functions in the interpreter outside of the interactive dashboards, load Modpol's library with: ``` $ lua[jit] @@ -50,13 +52,12 @@ In the interpreter, for a list of global functions and tables, use `modpol.menu( ## Storage -By default, a data directory named "data" will be created in this directory. `/data` will contain a log file and serialized program data files. +The persistent storage method may be chosen in `modpol.lua`. If available, Modpol uses Minetest's built-in StorageRef system for Minetest 5.*. If that is not available, or in CLI mode, data will be stored in a data directory at `modpol_core/data/`. This will contain a log file and serialized program data files. -Another storage method may be chosen in `modpol.lua`. A StorageRef-based method for Minetest 5.* is included: `storage-mod_storage.lua`. ## Design philosophy -Modpol seeks to implement a theoretical framework, also called "[modular politics](https://metagov.org/modpol)," which proposes these design goals: +Modpol seeks to implement a theoretical framework called "[modular politics](https://metagov.org/modpol)," which proposes these design goals: * *Modularity*: Platform operators and community members should have the ability to construct systems by creating, importing, and arranging composable parts together as a coherent whole. * *Expressiveness*: The governance layer should be able to implement as wide a range of processes as possible. From c8d527dba816f624ed7c8454b434670c59e22aae Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Fri, 31 Dec 2021 00:50:32 -0700 Subject: [PATCH 18/26] Have basic checkbox_query working in CLI core interactions, not yet attempted in Minetest --- modpol_core/api.lua | 3 ++ modpol_core/interactions/interactions.lua | 59 ++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/modpol_core/api.lua b/modpol_core/api.lua index 05755ca..bcca3e6 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -30,3 +30,6 @@ dofile (localdir .. "/modules/remove_process.lua") dofile (localdir .. "/modules/rename_org_consent.lua") dofile (localdir .. "/modules/send_token.lua") dofile (localdir .. "/modules/tokenomics.lua") + + +dofile (localdir .. "/modules/checkbox_test.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index b047c10..96c9055 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -60,11 +60,11 @@ function modpol.interactions.dashboard(user) if modpol.orgs.array[tonumber(sel)] then local sel_org = modpol.orgs.array[tonumber(sel)].name modpol.interactions.org_dashboard(user, sel_org) - else + else print("Org id not found") modpol.interactions.dashboard(user) - end - + end + elseif sel == "U" or sel == "u" then print("Access which user?") sel = io.read() @@ -276,7 +276,7 @@ function modpol.interactions.display(user, title, message, completion) output = message else modpol.interactions.message( - self.initiator, "Error: message not typed for display") + self.initiator, "Error: message not typed for display") modpol.interactions.message( self.initiator, "Error: input not typed for display") if completion then completion() else @@ -318,7 +318,7 @@ function modpol.interactions.dropdown_query(user, label, options, func) options_display = options_display .. "Select number:" if options_number == 0 then print("Error: No options given for dropdown") - return nil + return nil end -- begin displaying print(user .. ": " .. label) @@ -341,7 +341,54 @@ function modpol.interactions.dropdown_query(user, label, options, func) end end --- Function: modpol.binary_poll_user(user, question) +--- Function: modpol.interactions.checkbox_query +-- Allows user to select from a set of options +-- @param user Name of user (string) +-- @param label Query for user before options (string) +-- @param options table of options and their checked status in the form {{"option_1_string", true}, {"option_2_string", false}} +-- @param func function to be called with param "input", made up of the corrected table in the same format as the param options +function modpol.interactions.checkbox_query( + user, label, options, func) + -- set up options + local options_display = "" + local options_number = 0 + for i,v in ipairs(options) do + local checked = false + if v[2] then checked = true end + if checked then + checked = "x" + else + checked = " " + end + options_display = options_display..i..". [".. + checked.."] "..v[1].."\n" + options_number = options_number + 1 + end + if options_number == 0 then + print("Error: No options given for dropdown") + return nil + end + options_display = options_display.. + "List numbers to check (e.g., 1,2,5):" + -- begin displaying + print(user .. ": " .. label) + print(options_display) + -- read input and produce output + local answer = io.read() + local answer_table = {} + for match in (answer..","):gmatch("(.-)"..",") do + table.insert(answer_table, tonumber(match)) + end + local result_table = modpol.util.copy_table(options) + for i,v in ipairs(answer_table) do + -- flip the boolean on selected options + result_table[v][2] = not result_table[v][2] + end + func(result_table) +end + + +-- Function: modpol.interactions.binary_poll_user -- Params: user (string), question (string), func (function) -- func input: user input (string: y/n) -- Output: Applies "func" to user input From 28a05c584ce7db892a69aaead0af3e89d5e6db55 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sat, 1 Jan 2022 23:54:57 -0700 Subject: [PATCH 19/26] Minetest checkbox_query now working; checkbox-based change_modules module still needs debugging --- modpol_core/interactions/interactions.lua | 2 +- modpol_core/modules/change_modules.lua | 225 ++++++++------------- modpol_core/modules/display_processes.lua | 2 +- modpol_minetest/overrides/interactions.lua | 97 ++++++++- 4 files changed, 177 insertions(+), 149 deletions(-) diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 96c9055..8bf90de 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -369,7 +369,7 @@ function modpol.interactions.checkbox_query( return nil end options_display = options_display.. - "List numbers to check (e.g., 1,2,5):" + "List comma-separated options to flip (e.g., 1,2,5):" -- begin displaying print(user .. ": " .. label) print(options_display) diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index 58986c7..7878efe 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -10,7 +10,10 @@ local change_modules = { } change_modules.data = { - result = nil + result = nil, + modules_before = {}, + modules_after = {}, + summary = "", } change_modules.config = { @@ -18,149 +21,95 @@ change_modules.config = { function change_modules:initiate(result) self.data.result = result - -- Step 1: add or remove? - modpol.interactions.dropdown_query( - self.initiator, "Module change options:", - {"Add module","Remove module"}, + self.data.add_modules = {} + self.data.remove_modules = {} + local modules_before = {} + local modules_after = {} + -- generate self.config.modules table + for k, module in pairs(modpol.modules) do + if not modpol.modules[module.slug].hide then + local in_org = false + if self.org.modules[module.slug] then + in_org = true + end + table.insert( + modules_before, + {module.name.." ["..module.slug.."]", in_org}) + end + end + -- send query to user + modpol.interactions.checkbox_query( + self.initiator, + "Check the modules to activate in this org:", + modules_before, function(input) - if input == "Add module" then - self:add_module() - elseif input == "Remove module" then - self:remove_module() - end - end - ) -end - -function change_modules:add_module() - -- prepare module options - local available_modules = modpol.util.copy_table(modpol.modules) - for k,org_mod in pairs(self.org.modules) do - if available_modules[org_mod.slug] then - available_modules[org_mod.slug] = nil - end end - -- present module options - local modules_list = {} - for k,v in pairs(available_modules) do - table.insert(modules_list,v.name) - end - if #modules_list == 0 then - modpol.interactions.message( - self.initiator, "Org has all modules") - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - if self.data.result then self.data.result() end - self.org:delete_process(self.id) - return - end - table.sort(modules_list) - -- now ask which to add - modpol.interactions.dropdown_query( - self.initiator, "Choose a module to add:", - modules_list, - function(mod_choice) - -- confirm choice - modpol.interactions.binary_poll_user( - self.initiator, - "Confirm: propose to add module \"" .. - mod_choice .. "\"?", - function(input) - if input == "Yes" then - self:propose_change("add",mod_choice) - modpol.interactions.org_dashboard( - self.initiator, self.org.id) + -- identify changes + modules_after = input + for i,v in ipairs(modules_after) do + if v[2] ~= modules_before[i][2] then + if v[2] then + table.insert(self.data.add_modules, v[1]) + modpol.msg("add-insert: "..v[1]) else - self:add_module() + table.insert(self.data.remove_modules, v[1]) + modpol.msg("remove-insert: "..v[1]) end end - ) - end - ) -end - -function change_modules:remove_module() - -- prepare module options - local available_modules = {} - for k,org_mod in pairs(self.org.modules) do - if not org_mod.hide then - available_modules[org_mod.slug] = modpol.util.copy_table(org_mod) - end end - local modules_list = {} - local modules_count = 0 - for k,v in pairs(available_modules) do - table.insert(modules_list,v.name) - modules_count = modules_count + 1 - end - -- abort if no modules to remove - if modules_count == 0 then - modpol.interactions.message( - self.initiator, "Org has no modules") - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - if self.data.result then self.data.result() end - self.org:delete_process(self.id) - return - end - table.sort(modules_list) - -- now ask which to remove - modpol.interactions.dropdown_query( - self.initiator, "Choose a module to remove:", - modules_list, - function(mod_choice) - -- confirm choice - modpol.interactions.binary_poll_user( - self.initiator, - "Confirm: propose to remove module \"" .. mod_choice .. "\"?", - function(input) - if input == "Yes" then - self:propose_change("remove",mod_choice) - modpol.interactions.org_dashboard( - self.initiator, self.org.id) - else - self:remove_module() - end - end - ) - end - ) -end - ---- propose_change --- @field type "add" or "remove" -function change_modules:propose_change(type, mod_text) - self.org:call_module( - "consent",self.initiator, - { - prompt = "Do you consent to "..type.. - " this module in org "..self.org.name.. - ":\n"..mod_text, - votes_required = #self.org.members - }, - function() - if type == "add" then - for k,v in pairs(modpol.modules) do - if v.name == mod_text then - table.insert(self.org.modules,v) - end - end - modpol.interactions.message_org( - self.initiator,self.org.id, - "Consent reached:\nAdding \"" - ..mod_text.."\" to org "..self.org.name) - elseif type == "remove" then - local i = 0 - for k,v in pairs(self.org.modules) do - i = i + 1 - if v.name == mod_text then - self.org.modules[k] = nil - end - end - modpol.interactions.message_org( - self.initiator,self.org.id, - "Consent reached:\nRemoving \"" - ..mod_text.."\" from org "..self.org.name) end + -- abort if no changes + if self.data.add_modules == {} + and self.data.remove_modules == {} then + modpol.interactions.message( + self.initiator, "No module changes proposed") + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + self.org:delete_process(self.id) + return + end + -- proceed with consent + local query = "Accept module changes in org ".. + self.org.name.."?" + self.data.summary = "" + if #self.data.add_modules > 0 then + self.data.summary = self.data.summary.."\nAdd: ".. + table.concat(self.data.add_modules,", ") + elseif #self.data.remove_modules > 0 then + summary = "\nRemove: ".. + table.concat(self.data.remove_modules,", ") + end + self.org:call_module( + "consent", + self.initiator, + { + prompt = query..self.data.summary, + votes_required = #self.org.members + }, + function() + self:implement_change() + end) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) end) +end + +function change_modules:implement_change() + for i,v in ipairs(self.data.add_modules) do + local slug = string.match(v,"%[(.+)%]") + self.org.modules[slug] = + modpol.util.copy_table(modpol.modules[slug]) + table.sort(self.org.modules) + end + for i,v in ipairs(self.data.remove_modules) do + local slug = string.match(v,"%[(.+)%]") + self.org.modules[slug] = nil + table.sort(self.org.modules) + end + -- announce and shut down + modpol.interactions.message_org( + self.initiator, + self.org.id, + "Module changes applied to org "..self.org.name..":".. + self.data.summary) if self.data.result then self.data.result() end self.org:delete_process(self.id) end diff --git a/modpol_core/modules/display_processes.lua b/modpol_core/modules/display_processes.lua index 5e4fff2..c851fc5 100644 --- a/modpol_core/modules/display_processes.lua +++ b/modpol_core/modules/display_processes.lua @@ -38,7 +38,7 @@ function display_processes:initiate(result) and type(v2) ~= "table" then v2_string = tostring(v2) elseif type(v2) == "table" then - v2_string = table.concat(v2) + v2_string = tostring(v2) else v2_string = "Could not render" end diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index e39ac6a..b2ef941 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -66,13 +66,13 @@ function modpol.interactions.dashboard(user) "size[10,8]", "hypertext[0.5,0.5;9,1;title;Org dashboard]", "label[0.5,2;All orgs:]", - "dropdown[2,1.5;7,0.8;all_orgs;View...,"..formspec_list(all_orgs)..";;]", + "dropdown[2.5,1.5;7,0.8;all_orgs;View...,"..formspec_list(all_orgs)..";;]", "label[0.5,3;Your orgs:]", - "dropdown[2,2.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", + "dropdown[2.5,2.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "label[0.5,4;All users:]", - "dropdown[2,3.5;7,0.8;all_users;View...,"..formspec_list(all_users)..";;]", + "dropdown[2.5,3.5;7,0.8;all_users;View...,"..formspec_list(all_users)..";;]", "label[0.5,5;Pending ("..user_pending_count.."):]", - "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(user_pending)..";;]", + "dropdown[2.5,4.5;7,0.8;pending;View...,"..formspec_list(user_pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -181,13 +181,13 @@ function modpol.interactions.org_dashboard(user, org_string) minetest.formspec_escape(org.name)..""..membership_toggle(org.name).."]", "label[0.5,1.25;Parent: "..parent..membership_toggle(parent).."]", "label[0.5,2;Members:]", - "dropdown[2,1.5;7,0.8;members;View...,"..formspec_list(members)..";;]", + "dropdown[2.5,1.5;7,0.8;members;View...,"..formspec_list(members)..";;]", "label[0.5,3;Child orgs:]", - "dropdown[2,2.5;7,0.8;children;View...,"..formspec_list(children)..";;]", + "dropdown[2.5,2.5;7,0.8;children;View...,"..formspec_list(children)..";;]", "label[0.5,4;Modules:]", - "dropdown[2,3.5;7,0.8;modules;View...,"..formspec_list(modules)..";;]", + "dropdown[2.5,3.5;7,0.8;modules;View...,"..formspec_list(modules)..";;]", "label[0.5,5;Pending ("..num_pending.."):]", - "dropdown[2,4.5;7,0.8;pending;View...,"..formspec_list(pending)..";;]", + "dropdown[2.5,4.5;7,0.8;pending;View...,"..formspec_list(pending)..";;]", "button[0.5,7;1,0.8;refresh;Refresh]", "button[8.5,7;1,0.8;back;Back]", } @@ -286,7 +286,7 @@ function modpol.interactions.user_dashboard(viewer, user, completion) "size[10,8]", "hypertext[0.5,0.5;9,1;title;User: "..user.."]", "label[0.5,2;User's orgs:]", - "dropdown[2,1.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", + "dropdown[2.5,1.5;7,0.8;user_orgs;View...,"..formspec_list(user_orgs)..";;]", "button[0.5,7;1.5,0.8;message;Message]", "button_exit[8.5,7;1,0.8;close;Close]", } @@ -484,6 +484,85 @@ minetest.register_on_player_receive_fields(function (player, formname, fields) end end) + +--- Function: modpol.interactions.checkbox_query +-- Allows user to select from a set of options +-- @param user Name of user (string) +-- @param label Query for user before options (string) +-- @param options table of options and their checked status in the form {{"option_1_string", true}, {"option_2_string", false}} +-- @param func function to be called with param "input", made up of the corrected table in the same format as the param options +function modpol.interactions.checkbox_query( + user, label, options, func) + -- set up formspec + -- prepare options + local vertical = 0 + local checkbox_options = {} + for i,v in ipairs(options) do + local fs_line = "" + vertical = i * .5 + fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. + v[1]..";"..tostring(v[2]).."]" + table.insert(checkbox_options, fs_line) + end + local max = vertical * 4 + local bar_height = vertical / 2 + local formspec = { + "formspec_version[4]", + "size[10,8]", + "label[0.5,0.5;"..label.."]", + "scrollbaroptions[arrows=default;max="..max..";smallstep=10;largestep=100;thumbsize="..bar_height.."]", + "scrollbar[9,1;0.3,5.5;vertical;scroller;0]", + "scroll_container[0.5,1;9,5.5;scroller;vertical]", + } + -- prepare options + for i,v in ipairs(options) do + local fs_line = "" + local vertical = i * .5 + fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. + minetest.formspec_escape(v[1])..";" + ..tostring(v[2]).."]" + table.insert(formspec, fs_line) + end + table.insert(formspec,"scroll_container_end[]") + table.insert(formspec,"button[0.5,7;1.5,0.8;submit;Submit]") + table.insert( + formspec,"button_exit[8,7;1.5,0.8;cancel;Cancel]") + local formspec_string = table.concat(formspec, "") + -- present to players + minetest.show_formspec(user, "modpol:checkbox_query", formspec_string) + -- put func in _contexts + if _contexts[user] == nil then _contexts[user] = {} end + _contexts[user]["checkbox_query_func"] = func + _contexts[user]["checkbox_query_result"] = options +end +-- receive fields +minetest.register_on_player_receive_fields(function (player, formname, fields) + if formname == "modpol:checkbox_query" then + local pname = player:get_player_name() + -- start checking fields + if fields.cancel then + minetest.close_formspec(pname, formname) + elseif fields.submit then + -- send in result + minetest.close_formspec(pname, formname) + _contexts[pname].checkbox_query_func( + _contexts[pname].checkbox_query_result) + else + for k,v in pairs(fields) do + -- identify checkbox actions and flip bool + if string.find(k,"checkbox_") then + local index = tonumber( + string.match(k,"%d+")) + _contexts[pname].checkbox_query_result[index][2] = + not _contexts[pname].checkbox_query_result[index][2] + end + end + end + end +end) + + + -- Function: modpol.binary_poll_user(user, question, function) -- Overrides function at modpol/interactions.lua -- Params: user (string), question (string), func (function) From 2989bd34bdc055206dd283ec716ea82072cc85f2 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Sun, 2 Jan 2022 23:13:56 -0700 Subject: [PATCH 20/26] Completed checkbox_query and rewrite of change_modules module with checkboxes --- modpol_core/api.lua | 3 - modpol_core/interactions/interactions.lua | 15 +- .../modules/change_modules-dropdown.lua | 169 ++++++++++++++++++ modpol_core/modules/change_modules.lua | 8 +- modpol_core/util/misc.lua | 6 +- modpol_minetest/overrides/interactions.lua | 17 +- 6 files changed, 195 insertions(+), 23 deletions(-) create mode 100644 modpol_core/modules/change_modules-dropdown.lua diff --git a/modpol_core/api.lua b/modpol_core/api.lua index bcca3e6..05755ca 100644 --- a/modpol_core/api.lua +++ b/modpol_core/api.lua @@ -30,6 +30,3 @@ dofile (localdir .. "/modules/remove_process.lua") dofile (localdir .. "/modules/rename_org_consent.lua") dofile (localdir .. "/modules/send_token.lua") dofile (localdir .. "/modules/tokenomics.lua") - - -dofile (localdir .. "/modules/checkbox_test.lua") diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index 8bf90de..b30a7a4 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -187,7 +187,12 @@ function modpol.interactions.org_dashboard(user, org_string) print("Interact with which one (use [id] number)?") local to_interact = io.read() local process = org.processes[tonumber(to_interact)] - if not process then return end + if not process then + modpol.interactions.message( + user, "Not a pending process") + modpol.interactions.org_dashboard(user, org.id) + return + end if org:has_pending_actions(user) then if org.pending[user][process.id] then org:interact(process.id, user) @@ -381,8 +386,10 @@ function modpol.interactions.checkbox_query( end local result_table = modpol.util.copy_table(options) for i,v in ipairs(answer_table) do - -- flip the boolean on selected options - result_table[v][2] = not result_table[v][2] + if result_table[v] then + -- flip the boolean on selected options + result_table[v][2] = not result_table[v][2] + end end func(result_table) end @@ -433,5 +440,5 @@ end --testing command function modpol.msg(text) - modpol.interactions.message("TEST MSG: ",text) + modpol.interactions.message("TEST MSG",text) end diff --git a/modpol_core/modules/change_modules-dropdown.lua b/modpol_core/modules/change_modules-dropdown.lua new file mode 100644 index 0000000..58986c7 --- /dev/null +++ b/modpol_core/modules/change_modules-dropdown.lua @@ -0,0 +1,169 @@ +--- change_modules +-- @module change_modules +-- Depends on consent + +local change_modules = { + name = "Change modules (consent)", + slug = "change_modules", + desc = "Add or remove modules from the org with member consent", + hide = false; +} + +change_modules.data = { + result = nil +} + +change_modules.config = { +} + +function change_modules:initiate(result) + self.data.result = result + -- Step 1: add or remove? + modpol.interactions.dropdown_query( + self.initiator, "Module change options:", + {"Add module","Remove module"}, + function(input) + if input == "Add module" then + self:add_module() + elseif input == "Remove module" then + self:remove_module() + end + end + ) +end + +function change_modules:add_module() + -- prepare module options + local available_modules = modpol.util.copy_table(modpol.modules) + for k,org_mod in pairs(self.org.modules) do + if available_modules[org_mod.slug] then + available_modules[org_mod.slug] = nil + end end + -- present module options + local modules_list = {} + for k,v in pairs(available_modules) do + table.insert(modules_list,v.name) + end + if #modules_list == 0 then + modpol.interactions.message( + self.initiator, "Org has all modules") + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if self.data.result then self.data.result() end + self.org:delete_process(self.id) + return + end + table.sort(modules_list) + -- now ask which to add + modpol.interactions.dropdown_query( + self.initiator, "Choose a module to add:", + modules_list, + function(mod_choice) + -- confirm choice + modpol.interactions.binary_poll_user( + self.initiator, + "Confirm: propose to add module \"" .. + mod_choice .. "\"?", + function(input) + if input == "Yes" then + self:propose_change("add",mod_choice) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + else + self:add_module() + end + end + ) + end + ) +end + +function change_modules:remove_module() + -- prepare module options + local available_modules = {} + for k,org_mod in pairs(self.org.modules) do + if not org_mod.hide then + available_modules[org_mod.slug] = modpol.util.copy_table(org_mod) + end end + local modules_list = {} + local modules_count = 0 + for k,v in pairs(available_modules) do + table.insert(modules_list,v.name) + modules_count = modules_count + 1 + end + -- abort if no modules to remove + if modules_count == 0 then + modpol.interactions.message( + self.initiator, "Org has no modules") + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + if self.data.result then self.data.result() end + self.org:delete_process(self.id) + return + end + table.sort(modules_list) + -- now ask which to remove + modpol.interactions.dropdown_query( + self.initiator, "Choose a module to remove:", + modules_list, + function(mod_choice) + -- confirm choice + modpol.interactions.binary_poll_user( + self.initiator, + "Confirm: propose to remove module \"" .. mod_choice .. "\"?", + function(input) + if input == "Yes" then + self:propose_change("remove",mod_choice) + modpol.interactions.org_dashboard( + self.initiator, self.org.id) + else + self:remove_module() + end + end + ) + end + ) +end + +--- propose_change +-- @field type "add" or "remove" +function change_modules:propose_change(type, mod_text) + self.org:call_module( + "consent",self.initiator, + { + prompt = "Do you consent to "..type.. + " this module in org "..self.org.name.. + ":\n"..mod_text, + votes_required = #self.org.members + }, + function() + if type == "add" then + for k,v in pairs(modpol.modules) do + if v.name == mod_text then + table.insert(self.org.modules,v) + end + end + modpol.interactions.message_org( + self.initiator,self.org.id, + "Consent reached:\nAdding \"" + ..mod_text.."\" to org "..self.org.name) + elseif type == "remove" then + local i = 0 + for k,v in pairs(self.org.modules) do + i = i + 1 + if v.name == mod_text then + self.org.modules[k] = nil + end + end + modpol.interactions.message_org( + self.initiator,self.org.id, + "Consent reached:\nRemoving \"" + ..mod_text.."\" from org "..self.org.name) + end + end) + if self.data.result then self.data.result() end + self.org:delete_process(self.id) +end + +--- (Required) Add to module table +modpol.modules.change_modules = change_modules diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index 7878efe..a7bd7f2 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -49,16 +49,14 @@ function change_modules:initiate(result) if v[2] ~= modules_before[i][2] then if v[2] then table.insert(self.data.add_modules, v[1]) - modpol.msg("add-insert: "..v[1]) else table.insert(self.data.remove_modules, v[1]) - modpol.msg("remove-insert: "..v[1]) end end end -- abort if no changes - if self.data.add_modules == {} - and self.data.remove_modules == {} then + if #self.data.add_modules == 0 + and #self.data.remove_modules == 0 then modpol.interactions.message( self.initiator, "No module changes proposed") modpol.interactions.org_dashboard( @@ -74,7 +72,7 @@ function change_modules:initiate(result) self.data.summary = self.data.summary.."\nAdd: ".. table.concat(self.data.add_modules,", ") elseif #self.data.remove_modules > 0 then - summary = "\nRemove: ".. + self.data.summary = "\nRemove: ".. table.concat(self.data.remove_modules,", ") end self.org:call_module( diff --git a/modpol_core/util/misc.lua b/modpol_core/util/misc.lua index aa4ba47..202374a 100644 --- a/modpol_core/util/misc.lua +++ b/modpol_core/util/misc.lua @@ -7,7 +7,11 @@ function modpol.util.copy_table(t) local t2 = {} if pairs(t) then for k,v in pairs(t) do - t2[k] = v + if type(v) == "table" then + t2[k] = modpol.util.copy_table(v) + else + t2[k] = v + end end end return t2 diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index b2ef941..7b475f7 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -501,7 +501,8 @@ function modpol.interactions.checkbox_query( local fs_line = "" vertical = i * .5 fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. - v[1]..";"..tostring(v[2]).."]" + minetest.formspec_escape(v[1])..";".. + tostring(v[2]).."]" table.insert(checkbox_options, fs_line) end local max = vertical * 4 @@ -514,14 +515,9 @@ function modpol.interactions.checkbox_query( "scrollbar[9,1;0.3,5.5;vertical;scroller;0]", "scroll_container[0.5,1;9,5.5;scroller;vertical]", } - -- prepare options - for i,v in ipairs(options) do - local fs_line = "" - local vertical = i * .5 - fs_line = "checkbox[0,"..vertical..";checkbox_"..i..";".. - minetest.formspec_escape(v[1])..";" - ..tostring(v[2]).."]" - table.insert(formspec, fs_line) + -- insert options + for i,v in ipairs(checkbox_options) do + table.insert(formspec, v) end table.insert(formspec,"scroll_container_end[]") table.insert(formspec,"button[0.5,7;1.5,0.8;submit;Submit]") @@ -533,7 +529,8 @@ function modpol.interactions.checkbox_query( -- put func in _contexts if _contexts[user] == nil then _contexts[user] = {} end _contexts[user]["checkbox_query_func"] = func - _contexts[user]["checkbox_query_result"] = options + _contexts[user]["checkbox_query_result"] = + modpol.util.copy_table(options) end -- receive fields minetest.register_on_player_receive_fields(function (player, formname, fields) From 544edddb21da821b4f01a93fd0d164c83cdd6930 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 5 Jan 2022 22:40:41 +0000 Subject: [PATCH 21/26] Small bugfixes on testing --- modpol_core/modules/consent.lua | 2 +- modpol_core/orgs/process.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modpol_core/modules/consent.lua b/modpol_core/modules/consent.lua index 8e7f7dc..6c670ed 100644 --- a/modpol_core/modules/consent.lua +++ b/modpol_core/modules/consent.lua @@ -53,7 +53,7 @@ function consent:callback(member) self.org:delete_process(self.id) end modpol.interactions.org_dashboard( - member, self.org.name) + member, self.org.id) end ) end diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 9ebb03b..476447d 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -42,7 +42,7 @@ function modpol.orgs:call_module(module_slug, initiator, config, result) org = self, id = index, config = config, - data = module.data, + data = modpol.util.copy_table(module.data), slug = module_slug } From 5788ce199a2e84ef0023a80707a17cf387ef9725 Mon Sep 17 00:00:00 2001 From: Luke Miller Date: Wed, 5 Jan 2022 18:10:38 -0500 Subject: [PATCH 22/26] added parent/children ids to processes, modified delete process to be recursive, modules now have access to self:call_module --- modpol_core/orgs/process.lua | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 5f9d170..7a0976c 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -6,22 +6,7 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_ return end - local empty_index = nil - -- linear search for empty process slots (lazy deletion) - for k, v in ipairs(self.processes) do - if v == 'deleted' then - empty_index = k - break - end - end - - local index - -- attempts to fill empty spots in list, otherwise appends to end - if empty_index then - index = empty_index - else - index = #self.processes + 1 - end + local index = #self.processes + 1 local module = modpol.modules[module_slug] @@ -41,6 +26,7 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_ org = self, id = index, parent_id = parent_id, + children = {}, config = config, data = module.data, slug = module_slug @@ -48,7 +34,8 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_ -- call module wrapper for modules, passes its id to child process when called function new_process:call_module(module_slug, initiator, config, result) - self.org:call_module(module_slug, initiator, config, result, self.id) + local child_id = self.org:call_module(module_slug, initiator, config, result, self.id) + table.insert(self.children, child_id) end setmetatable(new_process, new_process.metatable) @@ -62,14 +49,21 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_ end function modpol.orgs:delete_process(id) - if self.processes[id] - and self.processes[id] ~= "deleted" then - local msg = "Deleting "..self.processes[id].slug.. - " process id "..id.." in org "..self.name - self:record(msg, self.processes[id].slug) - self:wipe_pending_actions(id) - self.processes[id] = 'deleted' - end + local process = self.processes[id] + if process and process ~= "deleted" then + -- recursively deletes any children + if #process.children > 0 then + for i, child_id in pairs(process.children) do + self:delete_process(child_id) + end + end + local msg = "Deleting " .. self.processes[id].slug .. " process id "..id.." in org "..self.name + modpol.ocutil.log(msg) + self:record(msg, self.processes[id].slug) + self:wipe_pending_actions(id) + -- sets process to 'deleted' in process table + self.processes[id] = 'deleted' + end end function modpol.orgs:add_pending_action(process_id, user, callback) From bdf4d86593d527c671ed890ab102ce17fc2734bf Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Wed, 5 Jan 2022 23:28:15 +0000 Subject: [PATCH 23/26] Removing a possibly unnecessary table copy on remove_process --- modpol_core/modules/remove_process.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modpol_core/modules/remove_process.lua b/modpol_core/modules/remove_process.lua index d382284..f8997ff 100644 --- a/modpol_core/modules/remove_process.lua +++ b/modpol_core/modules/remove_process.lua @@ -25,7 +25,7 @@ function remove_process:initiate(result) local available_processes = {} for k,process in pairs(self.org.processes) do if process ~= "deleted" then - available_processes[process.id] = modpol.util.copy_table(process) + available_processes[process.id] = process end end local process_list = {} From 65bb1aa53242fb30f11f9eead45149c4cc570859 Mon Sep 17 00:00:00 2001 From: Nathan Schneider Date: Thu, 6 Jan 2022 09:58:15 -0700 Subject: [PATCH 24/26] Added failsafes to interactions against 'deleted' processes --- modpol_core/interactions/interactions.lua | 12 +++++++----- modpol_minetest/overrides/interactions.lua | 10 ++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modpol_core/interactions/interactions.lua b/modpol_core/interactions/interactions.lua index b30a7a4..ee47e09 100644 --- a/modpol_core/interactions/interactions.lua +++ b/modpol_core/interactions/interactions.lua @@ -175,13 +175,15 @@ function modpol.interactions.org_dashboard(user, org_string) local processes = {} print("All processes: (* indicates pending)") for i,v in ipairs(org.processes) do - local active = '' - if org.pending[user] then - if org.pending[user][v.id] then - active = '*' + if v ~= "deleted" then + local active = '' + if org.pending[user] then + if org.pending[user][v.id] then + active = '*' + end end + print("["..v.id.."] "..v.slug..active) end - print("["..v.id.."] "..v.slug..active) end print() print("Interact with which one (use [id] number)?") diff --git a/modpol_minetest/overrides/interactions.lua b/modpol_minetest/overrides/interactions.lua index 7b475f7..69b3fe9 100644 --- a/modpol_minetest/overrides/interactions.lua +++ b/modpol_minetest/overrides/interactions.lua @@ -161,10 +161,12 @@ function modpol.interactions.org_dashboard(user, org_string) local num_pending = 0 if org.pending[user] then for k,v in pairs(org.pending[user]) do - local pending_string = org.processes[k].name - .." ["..k.."]" - table.insert(pending, pending_string) - num_pending = num_pending + 1 + if org.processes[k] ~= "deleted" then + local pending_string = org.processes[k].name + .." ["..k.."]" + table.insert(pending, pending_string) + num_pending = num_pending + 1 + end end end table.sort(pending) From a835799e110a5963650832abf75ab77aa479f274 Mon Sep 17 00:00:00 2001 From: Luke Miller Date: Thu, 6 Jan 2022 20:17:38 -0500 Subject: [PATCH 25/26] added delete process tree function, updated remove process module --- modpol_core/modules/remove_process.lua | 4 ++-- modpol_core/orgs/process.lua | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modpol_core/modules/remove_process.lua b/modpol_core/modules/remove_process.lua index f8997ff..39cf40c 100644 --- a/modpol_core/modules/remove_process.lua +++ b/modpol_core/modules/remove_process.lua @@ -64,7 +64,7 @@ function remove_process:initiate(result) function(input) if input == "Yes" then if process_mine then - self.org:delete_process(process_id) + self.org:delete_process_tree(process_id) modpol.interactions.message( self.initiator, "Removed process: "..process_choice) @@ -86,7 +86,7 @@ function remove_process:initiate(result) self.org.id, "Removing process: ".. process_choice) - self.org:delete_process(process_id) + self.org:delete_process_tree(process_id) modpol.interactions.org_dashboard( self.initiator, self.org.id) if result then result() end diff --git a/modpol_core/orgs/process.lua b/modpol_core/orgs/process.lua index 25c7ba6..f00ad2c 100644 --- a/modpol_core/orgs/process.lua +++ b/modpol_core/orgs/process.lua @@ -48,6 +48,14 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_ return index end +function modpol.orgs:get_root_process(id) + local process = self.processes[id] + while (process.parent_id) do + process = self.processes[process.parent_id] + end + return process +end + function modpol.orgs:delete_process(id) local process = self.processes[id] if process and process ~= "deleted" then @@ -66,6 +74,10 @@ function modpol.orgs:delete_process(id) end end +function modpol.orgs:delete_process_tree(id) + self:delete_process(self:get_root_process(id).id) +end + function modpol.orgs:add_pending_action(process_id, user, callback) self.pending[user] = self.pending[user] or {} self.pending[user][process_id] = callback From 0f479da5ffa298e74d1a045a0e24cbfcb8532bac Mon Sep 17 00:00:00 2001 From: Luke Miller Date: Fri, 7 Jan 2022 15:05:45 -0500 Subject: [PATCH 26/26] calling self:call_module instead of self.org:call_module, this should fix process deletion issues --- modpol_core/modules/add_child_org_consent.lua | 2 +- modpol_core/modules/change_modules-dropdown.lua | 2 +- modpol_core/modules/change_modules.lua | 2 +- modpol_core/modules/create_token.lua | 2 +- modpol_core/modules/join_org_consent.lua | 2 +- modpol_core/modules/remove_child_consent.lua | 2 +- modpol_core/modules/remove_member_consent.lua | 2 +- modpol_core/modules/remove_org_consent.lua | 2 +- modpol_core/modules/remove_process.lua | 2 +- modpol_core/modules/rename_org_consent.lua | 2 +- modpol_core/modules/tokenomics.lua | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modpol_core/modules/add_child_org_consent.lua b/modpol_core/modules/add_child_org_consent.lua index 4711387..6816b71 100644 --- a/modpol_core/modules/add_child_org_consent.lua +++ b/modpol_core/modules/add_child_org_consent.lua @@ -42,7 +42,7 @@ function add_child_org_consent:initiate(result) self.initiator, "Proposed child org: " .. input) -- initiate consent process - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/change_modules-dropdown.lua b/modpol_core/modules/change_modules-dropdown.lua index 58986c7..ae651ec 100644 --- a/modpol_core/modules/change_modules-dropdown.lua +++ b/modpol_core/modules/change_modules-dropdown.lua @@ -128,7 +128,7 @@ end --- propose_change -- @field type "add" or "remove" function change_modules:propose_change(type, mod_text) - self.org:call_module( + self:call_module( "consent",self.initiator, { prompt = "Do you consent to "..type.. diff --git a/modpol_core/modules/change_modules.lua b/modpol_core/modules/change_modules.lua index a7bd7f2..036e10b 100644 --- a/modpol_core/modules/change_modules.lua +++ b/modpol_core/modules/change_modules.lua @@ -75,7 +75,7 @@ function change_modules:initiate(result) self.data.summary = "\nRemove: ".. table.concat(self.data.remove_modules,", ") end - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/create_token.lua b/modpol_core/modules/create_token.lua index 6e54139..6f4c30c 100644 --- a/modpol_core/modules/create_token.lua +++ b/modpol_core/modules/create_token.lua @@ -28,7 +28,7 @@ function create_token:initiate(result) "Token name (alpha-numeric, no spaces):", function(input) self.config.token_name = input - self.org:call_module( + self:call_module( "tokenomics", self.initiator, { diff --git a/modpol_core/modules/join_org_consent.lua b/modpol_core/modules/join_org_consent.lua index 9bfa437..d8d78d8 100644 --- a/modpol_core/modules/join_org_consent.lua +++ b/modpol_core/modules/join_org_consent.lua @@ -24,7 +24,7 @@ function join_org_consent:initiate(result) self.org:delete_process(self.id) else self.data.result = result - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/remove_child_consent.lua b/modpol_core/modules/remove_child_consent.lua index ba3255e..c7ba25f 100644 --- a/modpol_core/modules/remove_child_consent.lua +++ b/modpol_core/modules/remove_child_consent.lua @@ -38,7 +38,7 @@ function remove_child_consent:initiate(result) children, function(input) self.data.child_to_remove = modpol.orgs.get_org(input) - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/remove_member_consent.lua b/modpol_core/modules/remove_member_consent.lua index fc118e1..4088e31 100644 --- a/modpol_core/modules/remove_member_consent.lua +++ b/modpol_core/modules/remove_member_consent.lua @@ -32,7 +32,7 @@ function remove_member_consent:initiate(result) self.org.members, function(input) self.data.member_to_remove = input - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/remove_org_consent.lua b/modpol_core/modules/remove_org_consent.lua index 30891a0..9dcb391 100644 --- a/modpol_core/modules/remove_org_consent.lua +++ b/modpol_core/modules/remove_org_consent.lua @@ -24,7 +24,7 @@ function remove_org_consent:initiate(result) self.org:delete_process(self.id) else self.data.result = result - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/remove_process.lua b/modpol_core/modules/remove_process.lua index 39cf40c..b5c5339 100644 --- a/modpol_core/modules/remove_process.lua +++ b/modpol_core/modules/remove_process.lua @@ -73,7 +73,7 @@ function remove_process:initiate(result) if result then result() end self.org:delete_process(self.id) else - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/rename_org_consent.lua b/modpol_core/modules/rename_org_consent.lua index 1b9aa47..551bf20 100644 --- a/modpol_core/modules/rename_org_consent.lua +++ b/modpol_core/modules/rename_org_consent.lua @@ -45,7 +45,7 @@ function rename_org_consent:initiate(result) "Proposed to change name of org " .. self.org.name .. " to " .. input) -- initiate consent process - self.org:call_module( + self:call_module( "consent", self.initiator, { diff --git a/modpol_core/modules/tokenomics.lua b/modpol_core/modules/tokenomics.lua index e968b29..d9aa306 100644 --- a/modpol_core/modules/tokenomics.lua +++ b/modpol_core/modules/tokenomics.lua @@ -47,7 +47,7 @@ function tokenomics:initiate(result) self.org:delete_process(self.id) else if self.config.consent then - self.org:call_module( + self:call_module( "consent", self.initiator, {