2 -- coras teamchat .. indev v0.5
4 -- adds a team chat for you and a couple friends, also prevents accidental sending of coordinates
5 -- to say something in teamchat either activate teammode in the dragonfire menu or use .t message
7 -- supports the Wisp encrypted whisper mod
9 -- .t to say something in team chat (or regular chat if team mode is on)
10 -- .tadd to add a team member
12 -- .tlist to list team
14 -- .coords to send a message containing coordinates
15 -- .mcoord to send a player your current coordinates
21 tchat.contains_coords(message) - returns true if the message contains coordinates (2d or 3d)
23 tchat.send(message) - send a message to teamchat, returns true if sent, nil if not
24 tchat.send_conditional(message, inverse?) - send a message to teamchat or regular chat, returns true if sent to teamchat, false if main chat, nil if not sent
25 tchat.send_coords(message) - send a message containing coordinates, true if sent, nil if not
27 tchat.whisper_coords(player) - DM current coords to a player
29 tchat.chat_clear() - clear chat widget
30 tchat.chat_set([]) - set chat widget
31 tchat.chat_append([] or message) - append to chat widget
33 tchat.team_add_player(player) - add player to team list
34 tchat.team_remove_player(player) - remove player from team list
35 tchat.team_clear() - clear team list
36 tchat.team_set([]) - set team list
41 tchat.chat: last few chat messages
43 tchat.team_online: online team list
44 tchat.players: currently online players
49 bool tchat_view_chat - if the team chat is shown
50 bool tchat_view_team_list - if the team list is shown
51 bool tchat_view_player_list - if the player list is shown
52 bool tchat_team_mode - if team mode is on
54 bool tchat_colorize_team - if true, team list will show all team members colored for who is online
55 bool tchat_use_wisp - if true, encrypt all messages using Wisp
57 str tchat_prefix_message - prefix for teamchat messages
58 str tchat_prefix_receive - prefix for received messages
59 str tchat_prefix_self - prefix for self sent messages
60 str tchat_prefix_send - prefix for sent messages
62 str tchat_blacklist - comma separated list of accounts that cannot send team chat messages (useful for secret alts)
64 num tchat_chat_length - chat length (messages, not lines)
65 num tchat_chat_width - chat width (columns)
72 local function init_settings(setting_table
)
73 for k
, v
in pairs(setting_table
) do
74 if minetest
.settings
:get(k
) == nil then
75 if type(v
) == "boolean" then
76 minetest
.settings
:set_bool(k
, v
)
78 minetest
.settings
:set(k
, v
)
85 tchat_view_chat
= false,
86 tchat_view_team_list
= true,
87 tchat_view_player_list
= true,
88 tchat_team_mode
= false,
90 tchat_colorize_team
= false,
92 tchat_prefix_message
= "TCHAT",
93 tchat_prefix_receive
= "From",
94 tchat_prefix_self
= "To Yourself",
95 tchat_prefix_send
= "To",
97 tchat_use_wisp
= false,
99 tchat_hide_sent
= true,
100 tchat_blacklist
= "",
102 tchat_chat_length
= 6,
103 tchat_chat_width
= 80
113 tchat
.team_online
= {}
118 local server_info
= minetest
.get_server_info()
119 local server_id
= server_info
.address
.. ':' .. server_info
.port
121 local max_total_chat_length
= 1024
123 local player_list_epoch
= 0
125 local message_prefix
= minetest
.settings
:get("tchat_prefix_message")
126 local message_receive
= minetest
.settings
:get("tchat_prefix_receive")
127 local message_receive_self
= minetest
.settings
:get("tchat_prefix_self")
128 local message_to
= minetest
.settings
:get("tchat_prefix_send")
130 local team_mode
= minetest
.settings
:get_bool("tchat_team_mode")
132 local use_wisp
= minetest
.settings
:get_bool("tchat_use_wisp")
134 local hide_sent
= minetest
.settings
:get_bool("tchat_hide_sent")
135 local blacklist
= string.split(minetest
.settings
:get("tchat_blacklist"))
137 local chat_length
= tonumber(minetest
.settings
:get("tchat_chat_length"))
138 local chat_width
= tonumber(minetest
.settings
:get("tchat_chat_width"))
140 local storage
= minetest
.get_mod_storage()
142 if storage
:get("tchat_team") == nil or storage
:get("tchat_team") == "null" then
143 storage
:set_string("tchat_team", "[]")
146 tchat
.team
= minetest
.parse_json(storage
:get_string("tchat_team"))
148 -- overrides contains_coords() the next time it runs
149 local message_confirmed_safe
= false
151 -- coordinate matching
152 local pattern
= "[-]?%d[.%d]*"
153 local space
= "[,%s]+"
154 local pattern_three
= pattern
.. space
.. pattern
.. space
.. pattern
155 local pattern_two
= pattern
.. space
.. pattern
158 local player_list_idx
166 local function apply(list
, func
, filter
)
168 for k
, v
in ipairs(list
) do
170 out
[#out
+ 1] = func(v
)
178 local function uniq(list
)
181 for k
, v
in ipairs(list
) do
190 -- limit a list to the last size elements
191 local function limit_list(list
, size
)
193 for i
= math
.max(1, #list
- size
), #list
do
194 out
[#out
+ 1] = list
[i
]
199 local function in_list(list
, value
)
200 for k
, v
in ipairs(list
) do
209 local function get_team_str()
210 if minetest
.settings
:get_bool("tchat_colorize_team") then
211 return table.concat(apply(tchat
.team
,
213 return minetest
.colorize("#00FFFF", value
)
216 return in_list(tchat
.team_online
, value
)
219 return table.concat(tchat
.team_online
, "\n")
224 local function display_chat()
225 return minetest
.localplayer
:hud_add({
226 hud_elem_type
= 'text',
228 text
= "Team Chat\n\n" .. chat_str
,
231 position
= {x
=0.01, y
=0.45},
232 scale
= {x
=0.9, y
=0.9},
233 alignment
= {x
=1, y
=1},
238 local function display_player_list()
239 return minetest
.localplayer
:hud_add({
240 hud_elem_type
= 'text',
241 name
= "Online Players",
242 text
= "Players\n\n" .. table.concat(tchat
.players
, "\n"),
245 position
= {x
=0.9, y
=0.01},
246 alignment
= {x
=1, y
=1},
251 -- should prob have all team members with online ones colored
252 local function display_team_list()
253 return minetest
.localplayer
:hud_add({
254 hud_elem_type
= 'text',
256 text
= "Team\n\n" .. get_team_str(),
259 position
= {x
=0.8, y
=0.01},
260 alignment
= {x
=1, y
=1},
265 local function auto_display(idx
, setting
, func
)
266 if minetest
.settings
:get_bool(setting
) then
272 minetest
.localplayer
:hud_remove(idx
)
279 local function auto_update(idx
, text
)
281 minetest
.localplayer
:hud_change(idx
, "text", text
)
285 local function update_team_online()
286 tchat
.team_online
= {}
287 for k
, v
in ipairs(tchat
.players
) do
288 if in_list(tchat
.team
, v
) then
289 tchat
.team_online
[#tchat
.team_online
+ 1] = v
294 local function update_chat_str()
296 for k
, v
in ipairs(limit_list(tchat
.chat
, chat_length
- 1)) do
297 chat_str
= chat_str
.. "\n" .. minetest
.wrap_text(v
, chat_width
)
299 chat_str
= table.concat(limit_list(string.split(chat_str
, "\n"), chat_length
- 1), "\n")
301 -- update chat (do it here so external mods can add to the chat)
302 auto_update(chat_idx
, "Team Chat\n\n" .. chat_str
)
305 local function team_add_self()
306 tchat
.team_add_player(minetest
.localplayer
:get_name())
314 function tchat
.contains_coords(message
)
315 if (not message_confirmed_safe
and (message
:find(pattern_three
) or message
:find(pattern_two
))) then
322 local function dm(player
, message
)
323 if wisp
== nil or not use_wisp
then
324 minetest
.send_chat_message("/msg " .. player
.." " .. message
)
326 wisp
.send(player
, message
, true)
331 function tchat
.send(message
, force_coords
, force_commands
)
332 if (tchat
.contains_coords(message
) and not force_coords
) or in_list(blacklist
, minetest
.localplayer
:get_name()) then
336 if message
:sub(1,1) == "/" and not force_commands
then
337 minetest
.display_chat_message("A /command was scheduled to be sent to team chat but wasn't sent.")
341 local me
= minetest
.localplayer
:get_name()
343 if not in_list(tchat
.team
, minetest
.localplayer
:get_name()) then
354 tchat
.chat_append(prepend
.. me
.. ": " .. message
)
356 for k
, p
in ipairs(tchat
.team_online
) do
358 dm(p
, message_prefix
.. " " .. message
)
364 function tchat
.send_conditional(message
, inverse
, force_coords
)
365 if tchat
.contains_coords(message
) and not force_coords
then
369 team_mode
= minetest
.settings
:get_bool("tchat_team_mode")
380 minetest
.send_chat_message(message
)
385 function tchat
.send_coords(message
)
386 message_confirmed_safe
= true
387 local ret
= tchat
.send_conditional(message
)
388 message_confirmed_safe
= false
393 function tchat
.whisper_coords(player
)
397 local coords
= minetest
.pos_to_string(vector
.round(minetest
.localplayer
:get_pos()))
398 minetest
.run_server_chatcommand("w", param
.. " " .. coords
)
403 local function autoclear_chat()
404 if #tchat
.chat
> max_total_chat_length
then
405 tchat
= limit_list(tchat
.chat
, max_chat_total_length
)
409 function tchat
.chat_clear()
414 function tchat
.chat_set(message_list
)
420 function tchat
.chat_append(message
)
421 tchat
.chat
[#tchat
.chat
+ 1] = message
424 minetest
.log("action", "[tchat] " .. minetest
.localplayer
:get_name() .. "@" .. server_id
.. " " .. message
)
428 -- popup chat if its closed
429 minetest
.settings
:set_bool("tchat_view_chat", true)
430 chat_idx
= auto_display(chat_idx
, "tchat_view_chat", display_chat
)
434 local function team_save()
435 storage
:set_string("tchat_team" , minetest
.write_json(tchat
.team
))
439 function tchat
.team_add_player(player
)
440 if not in_list(tchat
.team
, player
) then
441 tchat
.team
[#tchat
.team
+ 1] = player
447 function tchat
.team_remove_player(player
)
449 for k
, v
in ipairs(tchat
.team
) do
458 function tchat
.team_clear()
463 function tchat
.team_set(player_list
)
464 tchat
.team
= player_list
472 minetest
.register_on_sending_chat_message(function(message
)
473 if tchat
.contains_coords(message
) then
474 minetest
.display_chat_message("Message contained coordinates, be careful.")
478 team_mode
= minetest
.settings
:get_bool("tchat_team_mode")
480 if not team_mode
then
489 local function message_sent(message
)
490 return message
== "Message sent."
493 local function clean_message(message
)
494 -- dirty, strips out legitimate uses of the prefix
495 message
= message
:gsub(message_prefix
, "")
496 message
= message
:gsub("^" .. message_receive
, "")
497 message
= message
:gsub("^" .. message_receive_self
, minetest
.localplayer
:get_name())
499 message
= message
:gsub(": ", ": ")
500 message
= message
:match("^%s*(.-)%s*$")
505 -- greedily be the first in the receiving list (prob doesnt always work)
506 table.insert(minetest
.registered_on_receiving_chat_message
, 1, function(message
)
507 if hide_sent
and message_sent(message
) then
511 -- bit dirty, doesnt check the prefix position
512 if not message
:find(message_prefix
) then
516 local player
= message
:match(message_receive
.. " (.+): " .. message_prefix
)
518 local from_self
= message
:sub(1, message_receive_self
:len()) == message_receive_self
519 local received
= message
:sub(1, message_receive
:len()) == message_receive
520 local sent
= message
:sub(1, message_to
:len()) == message_to
522 if sent
and not from_self
then
526 if not from_self
and not in_list(tchat
.team_online
, player
) then
531 if from_self
or received
then
532 tchat
.chat_append(clean_message(message
))
538 wisp
.register_on_receive_split(function(player
, message
)
539 if message
:find(message_prefix
) then
540 tchat
.chat_append("E " .. player
.. ": " .. clean_message(message
))
546 minetest
.register_globalstep(function()
548 if player_list_epoch
< os
.time() + 2 then
549 -- update players, remove duplicates
550 tchat
.players
= minetest
.get_player_names()
551 table.sort(tchat
.players
)
552 tchat
.players
= uniq(tchat
.players
)
557 auto_update(player_list_idx
, "Players\n\n" .. table.concat(tchat
.players
, "\n"))
558 auto_update(team_list_idx
, "Team\n\n" .. get_team_str())
560 player_list_epoch
= os
.time()
563 -- display (if we need to)
564 if minetest
.localplayer
then
565 chat_idx
= auto_display(chat_idx
, "tchat_view_chat", display_chat
)
566 player_list_idx
= auto_display(player_list_idx
, "tchat_view_player_list", display_player_list
)
567 team_list_idx
= auto_display(team_list_idx
, "tchat_view_team_list", display_team_list
)
573 -- command/cheat interface
575 minetest
.register_chatcommand("t", {
576 params
= "<message>",
577 description
= "Send a message to your team chat, or regular chat if team mode is on.",
578 func
= function(message
)
579 if tchat
.contains_coords(message
) then
580 minetest
.display_chat_message("Message contained coordinates, be careful.")
583 tchat
.send_conditional(message
, true)
586 minetest
.register_chatcommand("tcoords", {
587 params
= "<message>",
588 description
= "Send a message containing coordinates to teamchat.",
589 func
= function(message
)
590 tchat
.send(message
, true)
593 minetest
.register_chatcommand("tlist", {
594 description
= "List your team.",
595 func
= function(param
)
596 minetest
.display_chat_message(table.concat(tchat
.team
, ", "))
599 minetest
.register_chatcommand("tadd", {
601 description
= "Add player to your team.",
602 func
= tchat
.team_add_player
604 minetest
.register_chatcommand("tdel", {
606 description
= "Remove player from your team.",
607 func
= tchat
.team_remove_player
609 minetest
.register_chatcommand("tclear", {
610 description
= "Clear team list.",
611 func
= tchat
.team_clear
614 minetest
.register_chatcommand("tchat_clear", {
615 description
= "Clear team chat widget.",
616 func
= tchat
.chat_clear
619 minetest
.register_chatcommand("coords", {
620 params
= "<message>",
621 description
= "Send message containing coordinates.",
622 func
= tchat
.send_coords
624 minetest
.register_chatcommand("mcoord", {
626 description
= "Whisper current coordinates to player.",
627 func
= tchat
.whisper_coords
631 -- minetest.register_cheat("Teamchat Mode", "Chat", "tchat_team_mode") -- doesnt work rn for some reason
632 minetest
.register_cheat("Show Team List", "Chat", "tchat_view_team_list")
633 minetest
.register_cheat("Show Player List", "Chat", "tchat_view_player_list")
634 minetest
.register_cheat("Show Teamchat", "Chat", "tchat_view_chat")