Maintenance and improvements

- Added mod.conf
- Changed .gui to .colors
- Autofill '#' in hex colors
- UI updates and improvements
master
random-geek 2022-02-14 00:30:39 -08:00
parent b8cc61843b
commit e944267217
4 changed files with 265 additions and 144 deletions

45
.luacheckrc Normal file
View File

@ -0,0 +1,45 @@
read_globals = {
"DIR_DELIM", "INIT",
"minetest", "core",
"dump", "dump2",
"Raycast",
"Settings",
"PseudoRandom",
"PerlinNoise",
"VoxelManip",
"SecureRandom",
"VoxelArea",
"PerlinNoiseMap",
"PcgRandom",
"ItemStack",
"AreaStore",
"vector",
table = {
fields = {
"copy",
"indexof",
"insert_all",
"key_value_swap",
"shuffle",
}
},
string = {
fields = {
"split",
"trim",
}
},
math = {
fields = {
"hypot",
"sign",
"factorial"
}
},
}

View File

@ -1,53 +1,59 @@
# chatcolor
A Minetest CSM which adds functionality for colouring messages from specific players.
[![](https://img.shields.io/badge/Minetest%20Forums-chatcolor-4E9A06)](https://forum.minetest.net/viewtopic.php?f=53&t=20345)
A Minetest client-side mod (CSM) which adds custom chat message coloring based on player name or message type.
## Features
* Colours regular, /me, and join/leave messages
* Colours names in server status messages
* Set any colour for any name
* Set default colours for each type of message
* Use either commands or user-interface
* Colors regular, /me, and join/leave messages
* Colors names in server status messages
* Set any color for any name
* Set default colors for each type of message
* Use either chat commands or GUI
## How to Use
### Colours
### Installation
Colours can be either a hex colour (such as `#00FF00`) or a HTML colour name (such as `plum`).
See [this great forum post](https://forum.minetest.net/viewtopic.php?f=53&t=17830) on how to install CSMs.
For a full list of HTML colours, see [this page](https://html-color-codes.info/color-names/).
### Colors
### .setcolor <name> <color>
Colors can be either a hex color (such as `#00FF00`) or a HTML color name (such as `plum`).
Colour messages from player `<name>` as `<color>`. To set a default for a certain type of message, use the names `default_chat`, `default_me`, or `default_join`.
For a full list of HTML colors, see [this page](https://html-color-codes.info/color-names/).
**Examples:**
### `.colors`
Displays a GUI for modifying colors.
### `.setcolor <name> <color>`
Color messages from player `<name>` as `<color>`. To set a default for a certain type of message, use the names `default_chat`, `default_me`, or `default_join`.
Examples:
`.setcolor lizzy123 #00FFFF`
`.setcolor default_me grey`
### .delcolor <name>
### `.delcolor <name>`
Delete a colour setting for a player. Their messages will then appear in a default colour.
Delete a color setting for a player. Their messages will then appear in a default color.
**Examples:**
Examples:
`.delcolor joe15`
### .listcolors
### `.listcolors`
Shows a list of all player and default colours in chat.
### .gui
Displays a user-interface which allows easier modification of colours.
Shows a list of all player and default colors in chat.
## Screenshots
![alt text](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture20.PNG "Coloured names is chat")
![Colored names in chat](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture20.PNG "Colored names in chat")
![alt text](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture21.PNG "Main user-interface")
![Main GUI](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture21.PNG "Main GUI")
![alt text](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture22.PNG "Modification view")
![Modification view](https://github.com/random-geek/Chat-color/blob/master/screenshots/Capture22.PNG "Modification view")

308
init.lua
View File

@ -1,184 +1,252 @@
-- Techy5's colored chat CSM
-- random-geek's colored chat CSM
local guiRow = 1 -- Which row in the GUI is selected
local data = minetest.get_mod_storage()
local forms = {"chat", "me", "join"}
local guiRow = 1 -- Which row in the GUI is selected
local default = "#FFFFFF" -- Default colour
local MESSAGE_TYPES = {"chat", "me", "join"}
local DEFAULT_COLOR = "#FFFFFF"
for i = 1,3 do -- Make sure all our defaults are in place.
local key = "default_" .. forms[i]
if not data:to_table().fields[key] then data:set_string(key, default) end
end
local chatSource = function(msg) -- Find the source of the message
if string.sub(msg, 1, 1) == "<" then -- Normal chat messages
local parts = string.split(msg, ">") -- Split it at the closing >
return {form = "chat", name = string.sub(parts[1], 2)} -- Return the first part excluding the first character
elseif string.sub(msg, 1, 2) == "* " then -- /me messages
local parts = string.split(msg, " ") -- Split the message before and after the name
return {form = "me", name = parts[2]}
elseif string.sub(msg, 1, 4) == "*** " then -- Join/leave messages
local parts = string.split(msg, " ") -- Split the message before and after the name
return {form = "join", name = parts[2]}
-- Make sure all our defaults are in place.
for _, type in ipairs(MESSAGE_TYPES) do
local key = "default_" .. type
if data:get_string(key) == "" then
data:set_string(key, DEFAULT_COLOR)
end
return false -- If nothing else returned, return false
end
local setColor = function(name, value)
local str, key
if not name or name == "" then -- Reject bad input
minetest.display_chat_message("Invalid setting name.")
-- Find the type and source of a chat message.
local function message_info(msg)
if string.sub(msg, 1, 1) == "<" then -- Normal chat messages (<player> message)
local parts = string.split(msg, ">")
return {type = "chat", name = string.sub(parts[1], 2)}
elseif string.sub(msg, 1, 2) == "* " then -- /me messages (* player message)
local parts = string.split(msg, " ")
return {type = "me", name = parts[2]}
elseif string.sub(msg, 1, 4) == "*** " then -- Join/leave messages (*** player joined/left the game.)
local parts = string.split(msg, " ")
return {type = "join", name = parts[2]}
else -- Unrecognized message type
return nil
end
end
-- Set player/default color.
-- name: player name or default_whatever
-- color: HTML string, hex color ('#' will be prepended if necessary), or nil to delete entry.
local function set_color(name, color)
if not name or name == "" then
minetest.display_chat_message("Player or setting name required.")
return
elseif not string.match(name, "^[%a%d-_]+$") then
minetest.display_chat_message(string.format("Invalid player or setting name '%s'.", name))
return
elseif color == "" then
minetest.display_chat_message("Color (hex color or color name) required.")
return
end
if string.len(name) > 8 and string.sub(name, 1, 8) == "default_" then -- If we are setting a default colour
if not value then -- Can't delete defaults
local key
if string.sub(name, 1, 8) == "default_" then
if not color then
minetest.display_chat_message("Cannot delete defaults!")
return
end
key = name
else -- If we are setting a player colour
key = "player_" .. name -- Append player prefix
else
-- Note: Commands/GUI omit the player prefix.
key = "player_" .. name
end
-- Check color if it exists.
if color then
-- Prepend '#' to hex colors if necessary.
local newColor = color
if tonumber(newColor, 16) then
newColor = "#" .. newColor
end
if not minetest.colorspec_to_colorstring(newColor) then
minetest.display_chat_message(string.format("Invalid color name '%s'.", color))
return
end
data:set_string(key, newColor)
minetest.display_chat_message(
string.format("Set color for %s to %s.", name, minetest.colorize(newColor, newColor))
)
else -- Delete player color entry
data:set_string(key, "")
minetest.display_chat_message(string.format("Deleted color for %s.", name))
end
data:set_string(key, value) -- Set colour
if value then str = "set" else str = "deleted" end -- Nil values indicate deletion
minetest.display_chat_message("Color " .. str .. " sucessfully! (" .. name .. ")")
end
local getList = function(readable) -- Return nicely sorted array of colour defenitions (if readable is true, player prefix will be excluded)
-- Return a nicely sorted array of {name, color} pairs.
local function get_list()
local list = data:to_table().fields
local arr = {}
for key,value in pairs(list) do -- Get key and value for all pairs
if string.sub(key, 1, 7) == "player_" then -- Exclude defaults
if readable then key = string.sub(key, 8) end -- Isolate the player name
arr[#arr+1] = key .. "," .. value
-- List players, excluding player_ prefix.
for key, color in pairs(list) do
if string.sub(key, 1, 7) == "player_" then
local name = string.sub(key, 8)
arr[#arr + 1] = {name, color}
end
end
table.sort(arr) -- Sort alphabetically.
for i = 1,3 do -- List defaults at end
local key = "default_" .. forms[i] -- Get default setting key
local value = list[key] -- Get value for key
arr[#arr+1] = key .. "," .. value
-- Sort alphabetically.
table.sort(
arr,
function(a, b)
return a[1] < b[1]
end
)
-- List defaults at end
for _, type in ipairs(MESSAGE_TYPES) do
local key = "default_" .. type
local color = list[key]
arr[#arr + 1] = {key, color}
end
return arr -- Numerical index table in key,value format. Must be numerical index for sorting.
return arr
end
local getFormspec = function(modify, defaultText)
local function get_formspec(modify, defaultPlayer, defaultColor)
if not modify then -- Fetch main screen
local tableDef = ""
local list = getList(true) -- Get list of players
for i = 1,#list do -- Convert to formspec-friendly format
local item = string.split(list[i], ",")
tableDef = tableDef .. item[1] .. ",".. item[2] .. "," .. item[2] .. ","
local list = get_list()
-- Convert list to formspec-friendly format
local tableRows = {}
for _, row in ipairs(list) do
tableRows[#tableRows + 1] = row[1] .. "," .. row[2] .. "," .. row[2]
end
tableDef = string.sub(tableDef, 1, string.len(tableDef)-1) -- Remove trailing comma
local tableString = table.concat(tableRows, ",")
return [[
size[8,9, false]
label[1,0.5;Techy5's Colored Chat]
button[1,1;2,1;main_modify;Modify...]
button[3,1;2,1;main_delete;Delete]
button[5,1;2,1;main_add;Add...]
formspec_version[5]
size[8,9]
label[0.5,0.6;Colored Chat]
button[0.5,1;2.1,0.8;main_modify;Modify...]
button[2.9,1;2.2,0.8;main_delete;Delete]
button[5.4,1;2.1,0.8;main_add;Add...]
tablecolumns[text;color;text]
table[1,2;6,6;main_table;]] .. tableDef .. [[;]] .. tostring(guiRow) .. [[]
button_exit[1,8;2,1;exit;Exit]
table[0.5,2.1;7,5.3;main_table;]] .. tableString .. [[;]] .. guiRow .. [[]
button_exit[0.5,7.7;2.1,0.8;exit;Exit]
tooltip[main_modify;Change the color for the selected element]
tooltip[main_delete;Delete the selected element]
tooltip[main_add;Add a color definition]
]]
else -- Fetch modify screen
return [[size[8,3, false]
field[1.3,1.3;2,1;mod_player;Player;]] .. defaultText .. [[]
field[3.3,1.3;2,1;mod_color;HTML/hex color;]
button[5,1;2,1;mod_set;Set]
button[1,2;2,1;mod_back;<- Back]
return [[
formspec_version[5]
size[8,3.2]
field[0.5,0.8;3.4,0.8;mod_player;Player;]] .. defaultPlayer .. [[]
field[4.1,0.8;3.4,0.8;mod_color;HTML/hex color;]] .. defaultColor .. [[]
button[5,1.9;2.5,0.8;mod_set;Set]
button[0.5,1.9;2.5,0.8;mod_cancel;Cancel]
]]
end
end
minetest.register_chatcommand("setcolor", { -- Assign a colour to chat messages from a specific person
params = "<name> <color>",
description = "Colourize a specified player's chat messages",
minetest.register_on_formspec_input(function(formname, fields)
-- Ignore potential formspecs from other mods.
if string.sub(formname, 1, 10) ~= "chatcolor:" then
return
end
-- Update the selected row index if needed.
if fields.main_table then
local event = minetest.explode_table_event(fields.main_table)
if event.type == "CHG" or event.type == "DCL" then
guiRow = event.row
end
end
if fields.main_delete then
local list = get_list()
local key = list[guiRow][1]
set_color(key, nil)
minetest.show_formspec("chatcolor:maingui", get_formspec())
elseif fields.main_modify then
local row = get_list()[guiRow]
-- Get formspec and send selected name to modify screen
minetest.show_formspec("chatcolor:modify", get_formspec(true, row[1], row[2]))
elseif fields.main_add then
minetest.show_formspec("chatcolor:modify", get_formspec(true, "", ""))
elseif fields.mod_set and fields.mod_player and fields.mod_color then
set_color(fields.mod_player, fields.mod_color)
minetest.show_formspec("chatcolor:maingui", get_formspec())
elseif fields.mod_cancel then
minetest.show_formspec("chatcolor:maingui", get_formspec())
end
end)
minetest.register_chatcommand("colors", {
params = "",
description = "Display colored chat GUI.",
func = function(param)
local args = string.split(param, " ") -- Split up the arguments
setColor(args[1], args[2])
guiRow = 1 -- Select first row of table
minetest.show_formspec("chatcolor:maingui", get_formspec())
end
})
minetest.register_chatcommand("setcolor", { -- Assign a color to chat messages from a specific person
params = "<name> <color>",
description = "Colorize a specified player's chat messages.",
func = function(param)
local args = string.split(param, " ")
-- If color is empty, pass an empty string to avoid deleting the entry.
set_color(args[1], args[2] or "")
end
})
minetest.register_chatcommand("delcolor", {
params = "<name>",
description = "Set a specified player's chat messages to the default color",
description = "Set a specified player's chat messages to the default color.",
func = function(param)
setColor(param, nil) -- Setting a colour to nil deletes it.
set_color(param, nil)
end
})
minetest.register_chatcommand("listcolors", {
params = "",
description = "List player/color pairs",
description = "List player/color pairs.",
func = function(param)
local list = getList(true)
for i = 1,#list do -- Print list to chat
local item = string.split(list[i], ",")
minetest.display_chat_message(item[1] .. ", ".. minetest.colorize(item[2], item[2]))
local list = get_list()
for _, row in ipairs(list) do
minetest.display_chat_message(row[1] .. ", " .. minetest.colorize(row[2], row[2]))
end
end
})
minetest.register_chatcommand("gui", {
params = "",
description = "Display colored chat GUI",
func = function(param)
guiRow = 1 -- Select first row of table
minetest.show_formspec("chatcolor:maingui", getFormspec())
end
})
minetest.register_on_formspec_input(function(formname, fields)
if not string.find(formname, "chatcolor") then return end -- Avoid conflicts
if fields.main_table then guiRow = tonumber(string.match(fields.main_table, "%d+")) end -- Get the selected table row on change.
if fields.main_delete then
local list = getList(true)
local key = string.split(list[guiRow], ",")[1] -- From selected row number, find what entry is selected
setColor(key, nil)
minetest.show_formspec("chatcolor:maingui", getFormspec())
elseif fields.main_modify then
local list = getList(true)
local key = string.split(list[guiRow], ",")[1] -- Same as above
minetest.show_formspec("chatcolor:modify", getFormspec(true, key)) -- Get formspec and send selected name to modify screen
elseif fields.main_add then
minetest.show_formspec("chatcolor:modify", getFormspec(true, ""))
elseif fields.mod_set and fields.mod_player and fields.mod_color then
setColor(fields.mod_player, fields.mod_color)
minetest.show_formspec("chatcolor:maingui", getFormspec())
elseif fields.mod_back then
minetest.show_formspec("chatcolor:maingui", getFormspec())
end
end)
-- I don't remember if or why `register_on_mods_loaded` was necessary.
minetest.register_on_mods_loaded(function()
minetest.register_on_receiving_chat_message(function(message)
local msgPlain = minetest.strip_colors(message)
local source = chatSource(msgPlain)
local plain = minetest.strip_colors(message)
local info = message_info(plain)
if source then -- Normal chat/me/join messages
local key = "player_" .. source.name -- The setting name
local color = data:get_string(key) -- Get the desired colour
if color == "" then -- If no colour, set to default
color = data:get_string("default_" .. source.form)
if info then -- Normal chat/me/join messages
local color = data:get_string("player_" .. info.name)
if color == "" then -- If no color, set to default
color = data:get_string("default_" .. info.type)
end
message = minetest.colorize(color, msgPlain)
minetest.display_chat_message(message)
local colorized = minetest.colorize(color, plain)
minetest.display_chat_message(colorized)
return true -- Override the original chat
elseif string.sub(msgPlain, 1, 2) == "# " then -- /status message
elseif string.sub(plain, 1, 2) == "# " then -- /status message
local colorized = plain
local list = data:to_table().fields
for key,value in pairs(list) do -- Get key and value for all pairs
if string.sub(key, 1, 7) == "player_" then -- Exclude default settings
key = string.sub(key, 8) -- Isolate the player name
msgPlain = string.gsub(msgPlain, key, minetest.colorize(value, key)) -- Replace plain name with coloured version
for key, color in pairs(list) do
if string.sub(key, 1, 7) == "player_" then
local playerName = string.sub(key, 8)
-- Replace plain name with colored version
colorized = string.gsub(colorized, playerName, minetest.colorize(color, playerName))
end
end
minetest.display_chat_message(msgPlain)
minetest.display_chat_message(colorized)
return true -- Override the original chat
end
end)

2
mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = chatcolor
description = Adds custom coloring of chat messages based on player or message type.