ranks
commit
04f882e85e
|
@ -0,0 +1,78 @@
|
|||
Ranks API
|
||||
=========
|
||||
The ranks API allows you to register, unregister, and manage ranks.
|
||||
|
||||
`ranks.register(name, def)`
|
||||
|
||||
* Register a new rank
|
||||
* `name`: Name for rank (name cannot be `clear`)
|
||||
* `def`: See [#Rank definition]
|
||||
|
||||
`ranks.unregister(name)`
|
||||
|
||||
* Unregister a rank
|
||||
* `name`: Name of rank
|
||||
|
||||
`ranks.list_plaintext()`
|
||||
|
||||
* Returns a plaintext, comma-separated list of ranks
|
||||
|
||||
`ranks.get_rank(player)`
|
||||
|
||||
* Returns the player's rank or `nil`
|
||||
* `player`: PlayerRef or string
|
||||
|
||||
`ranks.get_def(name)`
|
||||
|
||||
* Returns the rank definition or `nil`
|
||||
* `name`: Name of rank
|
||||
|
||||
`ranks.update_privs(player)`
|
||||
|
||||
* Checks and updates player privileges based on rank definition
|
||||
* Returns `true` if successful, `nil` if player has no rank
|
||||
* Automatically called `on_joinplayer` and on `ranks.set_rank`
|
||||
* `player`: PlayerRef or string
|
||||
|
||||
`ranks.update_nametag(player)`
|
||||
|
||||
* Checks and updates player nametag based on rank definition
|
||||
* Returns `true` if successful, `nil` if player has no rank
|
||||
* Automatically called `on_joinplayer` and on `ranks.set_rank`
|
||||
* `player`: PlayerRef or string
|
||||
|
||||
`ranks.set_rank(player, rank)`
|
||||
|
||||
* Changes a player's rank
|
||||
* Returns `true` if successful, `nil` if rank doesn't exist
|
||||
* `player`: PlayerRef or string
|
||||
* `rank`: Name of rank
|
||||
|
||||
`ranks.remove_rank(player)`
|
||||
|
||||
* Removes all ranking information from a player
|
||||
* `player`: PlayerRef or string
|
||||
|
||||
`ranks.chat_send(name, message)`
|
||||
|
||||
* Simulates a chat message being sent to all players from a ranked player
|
||||
* `name`: Sender player name
|
||||
* `message`: Message to send
|
||||
|
||||
#### Rank definition
|
||||
```lua
|
||||
{
|
||||
prefix = "Moderator", -- Prefix to be shown on nametag and chat
|
||||
colour = {a = 255, r = 255, g = 83, b = 37}, -- A table of RGBA values, a single base colour (e.g. "red"), or a hex string
|
||||
strict_privs = false, -- Whether the rank should have a strict set of privileges (prevents privileges from being granted/revoked)
|
||||
grant_missing = true, -- Whether missing privileges should be automatically granted
|
||||
revoke_extra = false, -- Whether extra privileges should be automatically revoked
|
||||
privs = { -- Default privileges (usage modified by above items)
|
||||
interact = true,
|
||||
shout = true,
|
||||
fly = true,
|
||||
fast = true,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,16 @@
|
|||
MIT License
|
||||
Copyright (c) 2017 Elijah Duffy
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,60 @@
|
|||
![Screenshot](screenshot.png)
|
||||
|
||||
Advanced Ranks [ranks]
|
||||
=======================
|
||||
- GitHub: https://github.com/octacian/ranks
|
||||
- Download: https://github.com/octacian/ranks/archive/master.zip
|
||||
- Forum: https://forum.minetest.net/viewtopic.php?f=9&t=17640
|
||||
|
||||
**Notice:** Your experience with Ranks can be improved by using a development build of Minetest. In order to use Ranks at its full potential, you'll need to [build](https://forum.minetest.net/viewtopic.php?id=3837) Minetest yourself with [this commit](https://github.com/minetest/minetest/commit/bc53c82bcf923d11725e3de8565eb4c7e435cd2b) or newer (June 21st, 2017).
|
||||
|
||||
Ranks is an advanced ranks mod geared towards larger servers helps to both distiguish between players and make managing privileges much easier. With several ranks premade and a simplistic API, ranks is a good addition to any server, especially those with many players.
|
||||
|
||||
This mod was made in an effort to solve two problems. One of these is new players getting confused when they see moderators or administrators doing things that normal players cannot, resulting in repeated accusations of hacking. Ranks allows there to be no confusion between what a player should or should not be able to do, as their rank is displayed in both their nametag and as a prefix to chat messages sent by them.
|
||||
|
||||
Displaying the rank prefix on a player's nametag can be disabled by setting `ranks.prefix_nametag = false` in `minetest.conf`, while prefixes before chat messages can be disabled with `ranks.prefix_chat = false`. Ranks message prefixes are also supported even when used alongside my other chat-related mods, for example, [chat3](https://forum.minetest.net/viewtopic.php?t=17163).
|
||||
|
||||
Ranks also attempts to make privilege updates easier by allowing you to assign a set of privileges to each rank allowing a strict set of privileges for that rank, automatically granting missing privileges, and automatically revoking extra privileges (configurable per-rank). This means that when a player is given a rank with privilege definitions, they will automatically inherit the privileges specified. Whenever `/grant`, `/grantme`, or `/revoke` is called on a player, their privileges are updated according to rank and a warning is sent to the player who called the chatcommand if privileges are modified further.
|
||||
|
||||
### Configuration
|
||||
All of the features provided by ranks can be configured directly from the advanced settings menu in the ranks subsection of the top-level Mods section. You can also configure ranks directly from `minetest.conf` with the settings listed below.
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------------------- | ---- | ------- | ----------------------- |
|
||||
| ranks.prefix_chat | bool | true | Prefix chat messages |
|
||||
| ranks.prefix_nametag | bool | true | Prefix nametags |
|
||||
|
||||
### Packaged Ranks
|
||||
By default, four ranks are included with the ranks mod, however, they are only for decoration purposes and do not modify any privileges as they should be configured by each server owner.
|
||||
|
||||
* Admin (`admin`)
|
||||
* Moderator (`moderator`)
|
||||
* Guardian (`guardian`)
|
||||
* YouTube (`youtube`)
|
||||
|
||||
The above demonstrates that while ranks can be useful for managing privileges, it can also be a very nice form of progression/recognition.
|
||||
|
||||
### Usage
|
||||
Ranks introduces two chatcommands, `/getrank` and `/rank`. The first allows you to get a player's rank while the latter allows you to set a player's rank and requires the `rank` chatcommand. **Note**: the player whose username is the same as the administrator (defined by `name` in `minetest.conf`) will automatically be granted the `rank` privilege.
|
||||
|
||||
**`/rank` Usage:**
|
||||
```
|
||||
Name or operation is either a player name, or "list" to list ranks
|
||||
If the operation is "list", no new rank is needed
|
||||
Setting new rank to "clear" causes all rank information to be removed from the player
|
||||
|
||||
/rank <name or operation> <new rank>
|
||||
```
|
||||
|
||||
**`/getrank` Usage:**
|
||||
```
|
||||
Name is the name of the player whose rank you wish to retrieve
|
||||
If name is blank, your own rank is returned
|
||||
|
||||
/getrank <name>
|
||||
```
|
||||
|
||||
### Creating Ranks
|
||||
You can create your own ranks and learn about privilege management in the API documentation. It explains how to manage privileges and register ranks with the `ranks.register` function. Registrations can be made either in the `ranks.lua` file of the mod itself (where default ranks or unregistered), or they can be preferrably placed in a `ranks.lua` file inside the world directory.
|
||||
|
||||
If you don't want one of the built in ranks, you can either register a new rank with the same name (effectively overriding the rank) or learn about `ranks.unregister` which can also be called from the world file.
|
|
@ -0,0 +1 @@
|
|||
chat3?
|
|
@ -0,0 +1 @@
|
|||
Ranks is an advanced ranks mod geared towards larger servers helps to both distiguish between players and make managing privileges much easier. With several ranks premade and a simplistic API, ranks is a good addition to any server, especially those with many players.
|
|
@ -0,0 +1,380 @@
|
|||
-- ranks/init.lua
|
||||
|
||||
ranks = {}
|
||||
|
||||
local chat3_exists = minetest.get_modpath("chat3")
|
||||
local registered = {}
|
||||
local default
|
||||
|
||||
---
|
||||
--- API
|
||||
---
|
||||
|
||||
-- [local function] Get colour
|
||||
local function get_colour(colour)
|
||||
if type(colour) == "table" and minetest.rgba then
|
||||
return minetest.rgba(colour.r, colour.g, colour.b, colour.a)
|
||||
elseif type(colour) == "string" then
|
||||
return colour
|
||||
else
|
||||
return "#ffffff"
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Register rank
|
||||
function ranks.register(name, def)
|
||||
assert(name ~= "clear", "Invalid name \"clear\" for rank")
|
||||
|
||||
registered[name] = def
|
||||
|
||||
if def.default then
|
||||
default = name
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Unregister rank
|
||||
function ranks.unregister(name)
|
||||
registered[name] = nil
|
||||
end
|
||||
|
||||
-- [function] List ranks in plain text
|
||||
function ranks.list_plaintext()
|
||||
local list = ""
|
||||
for rank, i in pairs(registered) do
|
||||
if list == "" then
|
||||
list = rank
|
||||
else
|
||||
list = list..", "..rank
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
-- [function] Get player rank
|
||||
function ranks.get_rank(player)
|
||||
if type(player) == "string" then
|
||||
player = minetest.get_player_by_name(player)
|
||||
end
|
||||
|
||||
local rank = player:get_attribute("ranks:rank")
|
||||
if rank and registered[rank] then
|
||||
return rank
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Get rank definition
|
||||
function ranks.get_def(rank)
|
||||
if not rank then
|
||||
return
|
||||
end
|
||||
|
||||
return registered[rank]
|
||||
end
|
||||
|
||||
-- [function] Update player privileges
|
||||
function ranks.update_privs(player, trigger)
|
||||
if type(player) == "string" then
|
||||
player = minetest.get_player_by_name(player)
|
||||
end
|
||||
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
local rank = ranks.get_rank(player)
|
||||
if rank then
|
||||
-- [local function] Warn
|
||||
local function warn(msg)
|
||||
if msg and trigger and minetest.get_player_by_name(trigger) then
|
||||
minetest.chat_send_player(trigger, minetest.colorize("red", "Warning: ")..msg)
|
||||
end
|
||||
end
|
||||
|
||||
local def = registered[rank]
|
||||
if not def.privs then
|
||||
return
|
||||
end
|
||||
|
||||
if def.strict_privs == true then
|
||||
minetest.set_player_privs(name, def.privs)
|
||||
warn(name.."'s privileges have been reset to that of their rank (strict privileges)")
|
||||
return true
|
||||
end
|
||||
|
||||
local privs = minetest.get_player_privs(name)
|
||||
|
||||
if def.grant_missing == true then
|
||||
local changed = false
|
||||
for name, priv in pairs(def.privs) do
|
||||
if not privs[name] and priv == true then
|
||||
privs[name] = priv
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
|
||||
if changed then
|
||||
warn("Missing rank privileges have been granted to "..name)
|
||||
end
|
||||
end
|
||||
|
||||
if def.revoke_extra == true then
|
||||
local changed = false
|
||||
for name, priv in pairs(privs) do
|
||||
if not def.privs[name] then
|
||||
privs[name] = nil
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
|
||||
if changed then
|
||||
warn("Extra non-rank privileges have been revoked from "..name)
|
||||
end
|
||||
end
|
||||
|
||||
local admin = player:get_player_name() == minetest.settings:get("name")
|
||||
-- If owner, grant `rank` privilege
|
||||
if admin then
|
||||
local name = player:get_player_name()
|
||||
local privs = minetest.get_player_privs(name)
|
||||
privs["rank"] = true
|
||||
minetest.set_player_privs(name, privs)
|
||||
end
|
||||
|
||||
minetest.set_player_privs(name, privs)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Update player nametag
|
||||
function ranks.update_nametag(player)
|
||||
if minetest.settings:get("ranks.prefix_nametag") == "false" then
|
||||
return
|
||||
end
|
||||
|
||||
if type(player) == "string" then
|
||||
player = minetest.get_player_by_name(player)
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
local rank = ranks.get_rank(player)
|
||||
if rank then
|
||||
local def = ranks.get_def(rank)
|
||||
local colour = get_colour(def.colour)
|
||||
local prefix = def.prefix
|
||||
|
||||
if prefix then
|
||||
prefix = minetest.colorize(colour, prefix).." "
|
||||
else
|
||||
prefix = ""
|
||||
end
|
||||
|
||||
player:set_nametag_attributes({
|
||||
text = prefix..name,
|
||||
})
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Set player rank
|
||||
function ranks.set_rank(player, rank)
|
||||
if type(player) == "string" then
|
||||
player = minetest.get_player_by_name(player)
|
||||
end
|
||||
|
||||
if registered[rank] then
|
||||
-- Set attribute
|
||||
player:set_attribute("ranks:rank", rank)
|
||||
-- Update nametag
|
||||
ranks.update_nametag(player)
|
||||
-- Update privileges
|
||||
ranks.update_privs(player)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Remove rank from player
|
||||
function ranks.remove_rank(player)
|
||||
if type(player) == "string" then
|
||||
player = minetest.get_player_by_name(player)
|
||||
end
|
||||
|
||||
local rank = ranks.get_rank(player)
|
||||
if rank then
|
||||
local name = player:get_player_name()
|
||||
|
||||
-- Clear attribute
|
||||
player:set_attribute("ranks:rank", nil)
|
||||
-- Update nametag
|
||||
player:set_nametag_attributes({
|
||||
text = name,
|
||||
color = "#ffffff",
|
||||
})
|
||||
-- Update privileges
|
||||
local basic_privs =
|
||||
minetest.string_to_privs(minetest.settings:get("basic_privs") or "interact,shout")
|
||||
minetest.set_player_privs(name, basic_privs)
|
||||
end
|
||||
end
|
||||
|
||||
-- [function] Send prefixed message (if enabled)
|
||||
function ranks.chat_send(name, message)
|
||||
if minetest.settings:get("ranks.prefix_chat") ~= "false" then
|
||||
local rank = ranks.get_rank(name)
|
||||
if rank then
|
||||
local def = ranks.get_def(rank)
|
||||
if def.prefix then
|
||||
local colour = get_colour(def.colour)
|
||||
local prefix = minetest.colorize(colour, def.prefix)
|
||||
if chat3_exists then
|
||||
chat3.send(name, message, prefix.." ", "ranks")
|
||||
else
|
||||
minetest.chat_send_all(prefix.." <"..name.."> "..message)
|
||||
minetest.log("action", "CHAT: ".."<"..name.."> "..message)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
--- Registrations
|
||||
---
|
||||
|
||||
-- [privilege] Rank
|
||||
minetest.register_privilege("rank", {
|
||||
description = "Permission to use /rank chatcommand",
|
||||
give_to_singleplayer = false,
|
||||
})
|
||||
|
||||
-- Assign/update rank on join player
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if ranks.get_rank(player) then
|
||||
-- Update nametag
|
||||
ranks.update_nametag(player)
|
||||
-- Update privileges
|
||||
ranks.update_privs(player)
|
||||
else
|
||||
if ranks.default then
|
||||
ranks.set_rank(player, ranks.default)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Prefix messages if enabled
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
return ranks.chat_send(name, message)
|
||||
end)
|
||||
|
||||
-- [chatcommand] /rank
|
||||
minetest.register_chatcommand("rank", {
|
||||
description = "Set a player's rank",
|
||||
params = "<player> <new rank> / \"list\" | username, rankname / list ranks",
|
||||
privs = {rank = true},
|
||||
func = function(name, param)
|
||||
local param = param:split(" ")
|
||||
if #param == 0 then
|
||||
return false, "Invalid usage (see /help rank)"
|
||||
end
|
||||
|
||||
if #param == 1 and param[1] == "list" then
|
||||
return true, "Available Ranks: "..ranks.list_plaintext()
|
||||
elseif #param == 2 then
|
||||
if minetest.get_player_by_name(param[1]) then
|
||||
if ranks.get_def(param[2]) then
|
||||
if ranks.set_rank(param[1], param[2]) then
|
||||
if name ~= param[1] then
|
||||
minetest.chat_send_player(param[1], name.." set your rank to "..param[2])
|
||||
end
|
||||
|
||||
return true, "Set "..param[1].."'s rank to "..param[2]
|
||||
else
|
||||
return false, "Unknown error while setting "..param[1].."'s rank to "..param[2]
|
||||
end
|
||||
elseif param[2] == "clear" then
|
||||
ranks.remove_rank(param[1])
|
||||
return true, "Removed rank from "..param[1]
|
||||
else
|
||||
return false, "Invalid rank (see /rank list)"
|
||||
end
|
||||
else
|
||||
return false, "Invalid player \""..param[1].."\""
|
||||
end
|
||||
else
|
||||
return false, "Invalid usage (see /help rank)"
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- [chatcommand] /getrank
|
||||
minetest.register_chatcommand("getrank", {
|
||||
description = "Get a player's rank. If no player is specified, your own rank is returned.",
|
||||
params = "<name> | name of player",
|
||||
func = function(name, param)
|
||||
if param and param ~= "" then
|
||||
if minetest.get_player_by_name(param) then
|
||||
local rank = ranks.get_rank(param) or "No rank"
|
||||
return true, "Rank of "..param..": "..rank
|
||||
else
|
||||
return false, "Invalid player \""..name.."\""
|
||||
end
|
||||
else
|
||||
local rank = ranks.get_rank(name) or "No rank"
|
||||
return false, "Your rank: "..rank
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
---
|
||||
--- Overrides
|
||||
---
|
||||
|
||||
local grant = minetest.registered_chatcommands["grant"].func
|
||||
-- [override] /grant
|
||||
minetest.registered_chatcommands["grant"].func = function(name, param)
|
||||
local ok, msg = grant(name, param) -- Call original function
|
||||
|
||||
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
|
||||
if grantname then
|
||||
ranks.update_privs(grantname, name) -- Update privileges
|
||||
end
|
||||
|
||||
return ok, msg
|
||||
end
|
||||
|
||||
local grantme = minetest.registered_chatcommands["grantme"].func
|
||||
-- [override] /grantme
|
||||
minetest.registered_chatcommands["grantme"].func = function(name, param)
|
||||
local ok, msg = grantme(name, param) -- Call original function
|
||||
ranks.update_privs(name, name) -- Update privileges
|
||||
return ok, msg
|
||||
end
|
||||
|
||||
local revoke = minetest.registered_chatcommands["revoke"].func
|
||||
-- [override] /revoke
|
||||
minetest.registered_chatcommands["revoke"].func = function(name, param)
|
||||
local ok, msg = revoke(name, param) -- Call original function
|
||||
|
||||
local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
|
||||
if revokename then
|
||||
ranks.update_privs(revokename, name) -- Update privileges
|
||||
end
|
||||
|
||||
return ok, msg
|
||||
end
|
||||
|
||||
---
|
||||
--- Ranks
|
||||
---
|
||||
|
||||
-- Load default ranks
|
||||
dofile(minetest.get_modpath("ranks").."/ranks.lua")
|
||||
|
||||
local path = minetest.get_worldpath().."/ranks.lua"
|
||||
-- Attempt to load per-world ranks
|
||||
if io.open(path) then
|
||||
dofile(path)
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
-- ranks/ranks.lua
|
||||
|
||||
ranks.register("boss", {
|
||||
prefix = "BOSS",
|
||||
colour = {a = 255, r = 0, g = 255, b = 72},
|
||||
})
|
||||
|
||||
ranks.register("moderator", {
|
||||
prefix = "Moderator",
|
||||
colour = {a = 255, r = 0, g = 170, b = 255},
|
||||
})
|
||||
|
||||
ranks.register("head_moderator", {
|
||||
prefix = "Head Moderator",
|
||||
colour = {a = 255, r = 255, g = 72, b = 0},
|
||||
})
|
||||
|
||||
ranks.register("host", {
|
||||
prefix = "Host",
|
||||
colour = {a = 255, r = 0, g = 255, b = 72},
|
||||
})
|
||||
|
||||
ranks.register("lower_moderator", {
|
||||
prefix = "Lower Moderator",
|
||||
colour = {a = 255, r = 34, g = 163, b = 11},
|
||||
})
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 281 KiB |
|
@ -0,0 +1,7 @@
|
|||
# If this is enabled, chat send by a player with a rank including a defined
|
||||
# prefix is prefixed with the rank prefix.
|
||||
ranks.prefix_chat (Prefix Chat) bool true
|
||||
|
||||
# If this is enabled, player with a rank that has a prefix will have this
|
||||
# prefix shown in their nametags.
|
||||
ranks.prefix_nametag (Prefix Nametag) bool true
|
Loading…
Reference in New Issue