-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinit.lua
More file actions
301 lines (265 loc) · 9.39 KB
/
init.lua
File metadata and controls
301 lines (265 loc) · 9.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
-- random-geek's colored chat CSM
local MESSAGE_TYPES = {"chat", "me", "join", "leave", "dm"}
local DEFAULT_COLOR = "#FFFFFF"
local data = minetest.get_mod_storage()
local guiRow = 1 -- Which row in the GUI is selected
-- 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
end
-- Removes Minetest \x1b escape sequences (colors, translations) from a string.
--
-- This may give wrong output for malformed strings or backslash-escaped right
-- parentheses within an escape string. See unescape_enriched() in
-- minetest/src/util/string.h for a correct implementation.
local function remove_escapes(str)
-- Remove \x1b followed by anything in parentheses
str = string.gsub(str, "\x1b%(.-%)", "")
-- Remove \x1b followed by a single character
str = string.gsub(str, "\x1b.", "")
return str
end
-- Test remove_escapes()
-- assert(remove_escapes("\x1b\xb1(Escapes\x1b() are\x1bE cool!\x1b(T@__Az-1\\\\))") == "(Escapes are cool!)")
-- Find the type and source of a chat message.
local function message_info(richMsg)
-- Strip any translation data to get a plaintext English message.
local msg = remove_escapes(richMsg)
local type, name
if string.sub(msg, 1, 1) == "<" then -- Normal chat messages (<player> message)
type = "chat"
name = string.match(msg, "^<([1-9A-Za-z-_]+)> ")
elseif string.sub(msg, 1, 2) == "* " then -- /me messages (* player message)
type = "me"
name = string.match(msg, "^%* ([1-9A-Za-z-_]+) ")
elseif string.sub(msg, 1, 4) == "*** " then -- Join/leave messages (*** player joined/left the game.)
local tempName, typeStr = string.match(msg, "^%*%*%* ([1-9A-Za-z-_]+) (%a+)")
name = tempName
if typeStr == "joined" then
type = "join"
elseif typeStr == "left" then
type = "leave"
end
elseif string.sub(msg, 1, 8) == "DM from " then -- Direct messages (DM from player: message)
type = "dm"
-- PM was switched to DM in Minetest 5.1.0, this supports both.
name = string.match(msg, "^DM from ([1-9A-Za-z-_]+): ")
end
-- Note: either may be nil.
if type and name then
return {type = type, name = name}
end
-- Unrecognized message types will return nil.
end
local function table_contains(tab, target)
for _, val in ipairs(tab) do
if val == target then
return true
end
end
return false
end
-- Set player/default color.
-- name: player name or default_something
-- 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, "^[1-9A-Za-z-_]+$") 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
local key
if string.sub(name, 1, 8) == "default_" then
if not table_contains(MESSAGE_TYPES, string.sub(name, 9)) then
minetest.display_chat_message(string.format("No setting called '%s'.", name))
return
end
if not color then
minetest.display_chat_message("Cannot delete defaults!")
return
end
key = name
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
end
-- Return a nicely sorted array of {name, color} pairs.
local function get_list()
local list = data:to_table().fields
local arr = {}
-- 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
-- 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
end
local function get_formspec(modify, defaultPlayer, defaultColor)
if not modify then -- Fetch main screen
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
local tableString = table.concat(tableRows, ",")
return [[
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[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 [[
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_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 or fields.key_enter) 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)
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.",
func = function(param)
set_color(param, nil)
end
})
minetest.register_chatcommand("listcolors", {
params = "",
description = "List player/color pairs.",
func = function(param)
local list = get_list()
for _, row in ipairs(list) do
minetest.display_chat_message(row[1] .. ", " .. minetest.colorize(row[2], row[2]))
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 plain = minetest.strip_colors(message)
local info = message_info(plain)
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
local colorized = minetest.colorize(color, plain)
minetest.display_chat_message(colorized)
return true -- Override the original chat
elseif string.sub(plain, 1, 2) == "# " then -- /status message
local colorized = plain
local list = data:to_table().fields
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(colorized)
return true -- Override the original chat
end
end)
end)