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.
This commit is contained in:
Bill Niblock 2022-05-25 18:00:39 -04:00
parent c71a72a09a
commit f1553e6c8e
3 changed files with 397 additions and 0 deletions

38
README.md Normal file
View file

@ -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.

357
init.lua Normal file
View file

@ -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> [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 <player name>")
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 <player name>")
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,
})

2
mod.conf Normal file
View file

@ -0,0 +1,2 @@
name = collective
descriptions = Collective facilitates groups and group-related activities