From f1553e6c8e83251d14fcebea6138560f70323099 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Wed, 25 May 2022 18:00:39 -0400 Subject: [PATCH] First Upload of Collective This first upload includes a relatively full and untested version of Collective. Much should be done still. - README.md: Basic repository and code information - init.lua: The first implementation of collective. Entirely tested on a single-player server, meaning much of the multi-person aspects are currently untested. This is first priority. - mod.conf: Standard Minetest mod stuff TO-DO: - Test Collective on a multi-player server with multiple people. --- README.md | 38 ++++++ init.lua | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mod.conf | 2 + 3 files changed, 397 insertions(+) create mode 100644 README.md create mode 100644 init.lua create mode 100644 mod.conf diff --git a/README.md b/README.md new file mode 100644 index 0000000..6733cf6 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Collective + +Collective is a mod for [Minetest](https://www.minetest.net/) that facilitates +groups and group-related activities. + +Collective is a work-in-progress, and currently should be used at your own risk. +Functionality has not been sufficiently tested, nor completed. + +## Installation + +Collective has no dependencies. To use Collective, follow the [mod installation +instructions](https://wiki.minetest.net/Installing_Mods) from the wiki, or this +summary: + +- Create a folder in your mod directory called `collective` +- Unzip the [latest archive]() into that directory +- Enable the mod by adding `load_mod_collective = true` to your `world.mt` file. + +``` +minetest/ +├── ... +├── mods/ +│ ├── ... +│ └── collective/ +│ ├── mod.conf +│ └── init.lua +├── ... +└── worlds/ + ├── ... + └── your_world + ├── ... + └── world.mt +``` + +## Contributing + +Open a PR and ping me! I'm +[Vagabond](https://matrix.to/#/@vagabondazulien:exp.farm) on Matrix. diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..f6e753f --- /dev/null +++ b/init.lua @@ -0,0 +1,357 @@ +local mod_storage = minetest.get_mod_storage() +local collective = {} + +-- ################### +-- #### FUNCTIONS #### +-- ################### + +function collective.disband(name) + --[[ + Disbands name's collective. + + Params: + name[string]: current player's name + ]]-- + if not collective.in_collective(name) then + minetest.chat_send_player(name, "You're not in a collective.") + return + end + + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + pmeta:set_string("collective", "") + minetest.chat_send_player(name, "Disbanding collective.") + + local members = {} + members = minetest.deserialize(mod_storage:get_string(name)) + for i, p in pairs(members) do + if name ~= p then + local exists = minetest.get_player_by_name(p) + if exists then + local cmeta = p:get_meta() + local membership = cmeta:get_string("collective") + if membership == name then + cmeta:set_string("collective", "") + minetest.chat_send_player(p, name.."'s collective has been disbanded") + end + end + end + end + + mod_storage:set_string(name, "") +end + +function collective.invite(name, param) + --[[ + Invite users to join name's collective. + + Params: + name[string]: current player's name + param[list]: list of player name strings + ]]-- + if not collective.in_collective(name) then + minetest.chat_send_player(name, "You're not in a collective. Use /collective start to make one.") + return + end + + local players = {} + if #param > 0 then + players = param + else + minetest.chat_send_player(name, "Usage: /collective invite [player name]...") + return + end + + for i, p in pairs(players) do + local exists = minetest.get_player_by_name(p) + local membership = collective.in_collective(p) + if exists then + if membership then + minetest.chat_send_player(name, "Player "..p.." already in a collective.") + minetest.chat_send_player(p, "You've been invited to "..name.."'s collective, but you can only be in one collective at a time.") + else + table.append(mod_storage[name], p) + minetest.chat_send_player(p, "You've been invited to "..name.."'s collective. Type /collective join "..name.." to join.") + minetest.chat_send_player(name, "Player "..p.." invited to your collective.") + end + else + minetest.chat_send_player(name, "Player "..p.." doesn't exist!") + end + end +end + +function collective.join(name, param) + --[[ + Join a collective. Requires being invited by the collective owner + + Params: + name[string]: current player's name + param[list]: list of player name strings; expecting a single value + ]]-- + if collective.in_collective(name) then + minetest.chat_send_player(name, "You're already in a collective!") + return + end + + if #param ~= 1 then + minetest.chat_send_player(name, "Usage: /collective join ") + return + end + + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + local members = mod_storage:get_string(param) + + if members then + if collective.membership(name, members) then + pmeta:set_string("collective", param) + table.append(mod_storage[name], p) + + minetest.chat_send_player(name, "You've joined "..name.."'s collective.") + else + minetest.chat_send_player(name, param.." has not established a collective.") + end +end + +function collective.kick(name, param) + --[[ + Kick a player from name's collective. + + Params: + name[string]: current player's name + param[list]: list of player name strings; expecting a single value + ]]-- + if not collective.in_collective(name) then + minetest.chat_send_player(name, "You're not in a collective.") + return + end + + if #param ~= 1 then + minetest.chat_send_player(name, "Usage: /collective kick ") + return + end + + if name == param[1] then + minetest.chat_send_player(name, "Cannot kick self! Use /collective disband") + return + end + + local exists = minetest.get_player_by_name(param[1]) + local member = collective.membership(name, param[1]) + if exists then + if member then + minetest.chat_send_player(name, "Kicking "..param[1].." from your collective.") + collective.reform(name, param) + minetest.chat_send_player(param[1], "You have been kicked from "..name.."'s collective.") + else + minetest.chat_send_player(name, param[1].." is not a member of your collective.") + end + else + minetest.chat_send_player(name, param[1].." is not an active player.") + end +end + +function collective.leave(name) + --[[ + Leave a collective, if currently in one. If name is the owner of the + collective, also disband the collective. + + Params: + name[string]: current player's name + ]]-- + if not collective.in_collective(name) then + minetest.chat_send_player(name, "You're not in a collective.") + return + end + + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + local membership = pmeta:get_string("collective") + + if name == membership then + collective.disband(name) + else + collective.reform(membership, name) + pmeta:set_string("collective", "") + minetest.chat_send_player(name, "You have left "..membership.."'s collective.") + minetest.chat_send_player(membership, name.." has left your collective.") + end +end + +function collective.start(name, param) + --[[ + Start a collective, and invite players if provided. + + Params: + name[string]: current player's name + param[list]: list of player name strings + ]]-- + if collective.in_collective(name) then + minetest.chat_send_player(name, "You're already in a collective!") + return + end + + local players = {} + if #param > 0 then + players = param + end + + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + pmeta:set_string("collective", name) + mod_storage:set_string(name, minetest.serialize({name})) + + minetest.chat_send_player(name, "Collective established!") + collective.invite(name, players) +end + +function collective.status(name) + --[[ + Shows current collective information + + Params: + name[string]: current player's name + ]]-- + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + local player_collective = pmeta:get_string("collective") + minetest.chat_send_player(name, "Current collective: "..player_collective) + + local members = mod_storage:get_string(name) + minetest.chat_send_player(name, "Collective members: "..members) +end + +-- ########################## +-- #### HELPER FUNCTIONS #### +-- ########################## + +function collective.in_collective(name) + --[[ + Check if a player is in any collectives + + Params: + name[string]: player name + + Returns: + boolean; false if the player's "collective" metadata is "" or nil + true otherwise + ]]-- + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + local membership = pmeta:get_string("collective") + + if membership == "" or membership == nil then + return false + else + return true + end +end + +function collective.membership(name, param) + --[[ + Check if current player is a member of a specific collective + + Params: + name[string]: current player's name + param[list]: list of player name strings; expecting a single value + + Returns: + boolean; true if name is a member of param's collective + false otherwise + ]]-- + local members = mod_storage:get_string(param) + + if string.match(cmeta, name) then + return true + else + return false + end +end + +function collective.print_usage() + --[[ + Print the /collective usage message + + Returns: + string; the usage message + ]]-- + return "Usage: /collective [join,leave,start,disband] [player {,player...}]" +end + +function collective.reform(name, param) + --[[ + Reform name's collective without param + + Params: + name[string]: player's name; used for collective + param[list]: list of player name strings + ]]-- + local members = {} + members = minetest.deserialize(mod_storage:get_string(name)) + local new_members = {} + + -- For each member of the collective, and each player provided in param, if + -- the member does not match the param, add the member to the new_members + -- list. + -- + -- This is expensive. Is there a better way to handle this? + for i,m in pairs(members) do + for j,p in pairs(param) do + if not m == p then table.append(new_members, m) end + end + end + + mod_storage:set_string(name, minetest.serialize(new_members)) +end + +-- ####################### +-- #### CHAT COMMANDS #### +-- ####################### + +minetest.register_chatcommand("collective", { + func = function(name, param) + + if #param <= 0 then + return false, collective.print_usage() + end + + local player = minetest.get_player_by_name(name) + if not player then + return false, "You must be online to interact with the collective." + end + + local parsed = {} + for w in string.gmatch(param, "%g+") do + table.insert(parsed, w) + end + + local command = table.remove(parsed, 1) + if command == "start" then + collective.start(name, parsed) + return true + elseif command == "invite" then + collective.invite(name, parsed) + return true + elseif command == "join" then + collective.join(name, parsed) + return true + elseif command == "leave" then + collective.leave(name) + return true + elseif command == "kick" then + collective.leave(name, parsed) + return true + elseif command == "disband" then + collective.disband(name) + return true + elseif command == "status" then + collective.status(name) + return true + else + return false, collective.print_usage() + end + end, +}) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..9462a6a --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +name = collective +descriptions = Collective facilitates groups and group-related activities