diff --git a/builtin/game/chatcommands.lua b/builtin/game/chat.lua similarity index 98% rename from builtin/game/chatcommands.lua rename to builtin/game/chat.lua index b2a49863e..84559b064 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chat.lua @@ -1,4 +1,29 @@ --- Minetest: builtin/game/chatcommands.lua +-- Minetest: builtin/game/chat.lua + +-- +-- Chat message formatter +-- + +-- Implemented in Lua to allow redefinition +function core.format_chat_message(name, message) + local str = core.settings:get("chat_message_format") + local error_str = "Invalid chat message format - missing %s" + local i + + str, i = str:gsub("@name", name, 1) + if i == 0 then + error(error_str:format("@name"), 2) + end + + str, i = str:gsub("@message", message, 1) + if i == 0 then + error(error_str:format("@message"), 2) + end + + str = str:gsub("@timestamp", os.date("%H:%M:%S", os.time()), 1) + + return str +end -- -- Chat command handler diff --git a/builtin/game/init.lua b/builtin/game/init.lua index 04f0732e5..271e49be3 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -1,37 +1,37 @@ local scriptpath = core.get_builtin_path() -local commonpath = scriptpath.."common"..DIR_DELIM -local gamepath = scriptpath.."game"..DIR_DELIM +local commonpath = scriptpath .. "common" .. DIR_DELIM +local gamepath = scriptpath .. "game".. DIR_DELIM -- Shared between builtin files, but -- not exposed to outer context local builtin_shared = {} -dofile(commonpath.."vector.lua") +dofile(commonpath .. "vector.lua") -dofile(gamepath.."constants.lua") -assert(loadfile(gamepath.."item.lua"))(builtin_shared) -dofile(gamepath.."register.lua") +dofile(gamepath .. "constants.lua") +assert(loadfile(gamepath .. "item.lua"))(builtin_shared) +dofile(gamepath .. "register.lua") if core.settings:get_bool("profiler.load") then - profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") + profiler = dofile(scriptpath .. "profiler" .. DIR_DELIM .. "init.lua") end dofile(commonpath .. "after.lua") -dofile(gamepath.."item_entity.lua") -dofile(gamepath.."deprecated.lua") -dofile(gamepath.."misc.lua") -dofile(gamepath.."privileges.lua") -dofile(gamepath.."auth.lua") +dofile(gamepath .. "item_entity.lua") +dofile(gamepath .. "deprecated.lua") +dofile(gamepath .. "misc.lua") +dofile(gamepath .. "privileges.lua") +dofile(gamepath .. "auth.lua") dofile(commonpath .. "chatcommands.lua") -dofile(gamepath.."chatcommands.lua") +dofile(gamepath .. "chat.lua") dofile(commonpath .. "information_formspecs.lua") -dofile(gamepath.."static_spawn.lua") -dofile(gamepath.."detached_inventory.lua") -assert(loadfile(gamepath.."falling.lua"))(builtin_shared) -dofile(gamepath.."features.lua") -dofile(gamepath.."voxelarea.lua") -dofile(gamepath.."forceloading.lua") -dofile(gamepath.."statbars.lua") +dofile(gamepath .. "static_spawn.lua") +dofile(gamepath .. "detached_inventory.lua") +assert(loadfile(gamepath .. "falling.lua"))(builtin_shared) +dofile(gamepath .. "features.lua") +dofile(gamepath .. "voxelarea.lua") +dofile(gamepath .. "forceloading.lua") +dofile(gamepath .. "statbars.lua") profiler = nil diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 931fe26da..21da565ca 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1058,6 +1058,10 @@ disable_anticheat (Disable anticheat) bool false # This option is only read when server starts. enable_rollback_recording (Rollback recording) bool false +# Format of player chat messages. The following strings are valid placeholders: +# @name, @message, @timestamp (optional) +chat_message_format (Chat message format) string <@name> @message + # A message to be displayed to all clients when the server shuts down. kick_msg_shutdown (Shutdown message) string Server shutting down. diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 41061ed87..840841e6b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4156,6 +4156,12 @@ Chat * `minetest.chat_send_all(text)` * `minetest.chat_send_player(name, text)` +* `minetest.format_chat_message(name, message)` + * Used by the server to format a chat message, based on the setting `chat_message_format`. + Refer to the documentation of the setting for a list of valid placeholders. + * Takes player name and message, and returns the formatted string to be sent to players. + * Can be redefined by mods if required, for things like colored names or messages. + * **Only** the first occurrence of each placeholder will be replaced. Environment access ------------------ diff --git a/minetest.conf.example b/minetest.conf.example index fe9d0e7f9..fc02e70cf 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1277,6 +1277,11 @@ # type: bool # enable_rollback_recording = false +# Format of player chat messages. The following strings are valid placeholders: +# @name, @message, @timestamp (optional) +# type: string +chat_message_format = <@name> @message + # A message to be displayed to all clients when the server shuts down. # type: string # kick_msg_shutdown = Server shutting down. diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4c39a44e1..b6ab7c9a0 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -356,6 +356,7 @@ void set_default_settings(Settings *settings) settings->setDefault("kick_msg_crash", "This server has experienced an internal error. You will now be disconnected."); settings->setDefault("ask_reconnect_on_crash", "false"); + settings->setDefault("chat_message_format", "<@name> @message"); settings->setDefault("profiler_print_interval", "0"); settings->setDefault("active_object_send_range_blocks", "4"); settings->setDefault("active_block_range", "3"); diff --git a/src/gettime.h b/src/gettime.h index f20728dfd..66efef1d7 100644 --- a/src/gettime.h +++ b/src/gettime.h @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes.h" #include #include diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp index 3b461a2a3..1ce2f9d45 100644 --- a/src/script/cpp_api/s_server.cpp +++ b/src/script/cpp_api/s_server.cpp @@ -168,3 +168,25 @@ void ScriptApiServer::on_shutdown() runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); } +std::string ScriptApiServer::formatChatMessage(const std::string &name, + const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push function onto stack + lua_getglobal(L, "core"); + lua_getfield(L, -1, "format_chat_message"); + + // Push arguments onto stack + lua_pushstring(L, name.c_str()); + lua_pushstring(L, message.c_str()); + + // Actually call the function + lua_call(L, 2, 1); + + // Fetch return value + std::string ret = lua_tostring(L, -1); + lua_pop(L, 1); + + return ret; +} diff --git a/src/script/cpp_api/s_server.h b/src/script/cpp_api/s_server.h index 769939d3f..a4cede84d 100644 --- a/src/script/cpp_api/s_server.h +++ b/src/script/cpp_api/s_server.h @@ -36,14 +36,18 @@ public: // Calls on_shutdown handlers void on_shutdown(); + // Calls core.format_chat_message + std::string formatChatMessage(const std::string &name, + const std::string &message); + /* auth */ bool getAuth(const std::string &playername, - std::string *dst_password, - std::set *dst_privs); + std::string *dst_password, + std::set *dst_privs); void createAuth(const std::string &playername, - const std::string &password); + const std::string &password); bool setPassword(const std::string &playername, - const std::string &password); + const std::string &password); private: void getAuthHandler(); void readPrivileges(int index, std::set &result); diff --git a/src/server.cpp b/src/server.cpp index 7e6208711..27388e666 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1571,7 +1571,7 @@ void Server::SendChatMessage(session_t peer_id, const ChatMessage &message) } void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec, - const std::string &formname) + const std::string &formname) { NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id); if (formspec.empty()){ @@ -2863,28 +2863,28 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna { // If something goes wrong, this player is to blame RollbackScopeActor rollback_scope(m_rollback, - std::string("player:") + name); + std::string("player:") + name); if (g_settings->getBool("strip_color_codes")) wmessage = unescape_enriched(wmessage); if (player) { switch (player->canSendChatMessage()) { - case RPLAYER_CHATRESULT_FLOODING: { - std::wstringstream ws; - ws << L"You cannot send more messages. You are limited to " - << g_settings->getFloat("chat_message_limit_per_10sec") - << L" messages per 10 seconds."; - return ws.str(); - } - case RPLAYER_CHATRESULT_KICK: - DenyAccess_Legacy(player->getPeerId(), - L"You have been kicked due to message flooding."); - return L""; - case RPLAYER_CHATRESULT_OK: - break; - default: - FATAL_ERROR("Unhandled chat filtering result found."); + case RPLAYER_CHATRESULT_FLOODING: { + std::wstringstream ws; + ws << L"You cannot send more messages. You are limited to " + << g_settings->getFloat("chat_message_limit_per_10sec") + << L" messages per 10 seconds."; + return ws.str(); + } + case RPLAYER_CHATRESULT_KICK: + DenyAccess_Legacy(player->getPeerId(), + L"You have been kicked due to message flooding."); + return L""; + case RPLAYER_CHATRESULT_OK: + break; + default: + FATAL_ERROR("Unhandled chat filtering result found."); } } @@ -2912,10 +2912,8 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna line += L"-!- You don't have permission to shout."; broadcast_line = false; } else { - line += L"<"; - line += wname; - line += L"> "; - line += wmessage; + line += narrow_to_wide(m_script->formatChatMessage(name, + wide_to_narrow(wmessage))); } /*