aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/client/chatcommands.lua12
-rw-r--r--builtin/settingtypes.txt3
-rw-r--r--doc/client_lua_api.md6
-rw-r--r--minetest.conf.example4
-rw-r--r--src/client.cpp52
-rw-r--r--src/client.h8
-rw-r--r--src/defaultsettings.cpp1
-rw-r--r--src/script/lua_api/l_client.cpp19
-rw-r--r--src/script/lua_api/l_client.h6
9 files changed, 108 insertions, 3 deletions
diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua
index 20f98a293..ed43a6140 100644
--- a/builtin/client/chatcommands.lua
+++ b/builtin/client/chatcommands.lua
@@ -51,3 +51,15 @@ core.register_chatcommand("disconnect", {
core.disconnect()
end,
})
+
+core.register_chatcommand("clear_chat_queue", {
+ description = core.gettext("Clear the out chat queue"),
+ func = function(param)
+ core.clear_out_chat_queue()
+ return true, core.gettext("The out chat queue is now empty")
+ end,
+})
+
+function core.run_server_chatcommand(cmd, param)
+ core.send_chat_message("/" .. cmd .. " " .. param)
+end
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 6dd3e825b..c710afdcf 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -315,6 +315,9 @@ serverlist_url (Serverlist URL) string servers.minetest.net
# File in client/serverlist/ that contains your favorite servers displayed in the Multiplayer Tab.
serverlist_file (Serverlist file) string favoriteservers.txt
+# Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited
+max_out_chat_queue_size (Maximum size of the out chat queue) int 20
+
[*Graphics]
[**In-Game]
diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md
index e2841dac1..3bd1e3567 100644
--- a/doc/client_lua_api.md
+++ b/doc/client_lua_api.md
@@ -721,6 +721,12 @@ Call these functions only at load time!
### Player
* `minetest.get_wielded_item()`
* Returns the itemstack the local player is holding
+* `minetest.send_chat_message(message)`
+ * Act as if `message` was typed by the player into the terminal.
+* `minetest.run_server_chatcommand(cmd, param)`
+ * Alias for `minetest.send_chat_message("/" .. cmd .. " " .. param)`
+* `minetest.clear_out_chat_queue()`
+ * Clears the out chat queue
* `minetest.localplayer`
* Reference to the LocalPlayer object. See [`LocalPlayer`](#localplayer) class reference for methods.
diff --git a/minetest.conf.example b/minetest.conf.example
index d6a57d681..d7c309c6d 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -343,6 +343,10 @@
# type: string
# serverlist_file = favoriteservers.txt
+# Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited
+# type: int
+# max_out_chat_queue_size = 20
+
## Graphics
### In-Game
diff --git a/src/client.cpp b/src/client.cpp
index d0c90b108..0f689a714 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -83,6 +83,7 @@ Client::Client(
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
m_address_name(address_name),
m_server_ser_ver(SER_FMT_VER_INVALID),
+ m_last_chat_message_sent(time(NULL)),
m_password(password),
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
m_media_downloader(new ClientMediaDownloader()),
@@ -388,6 +389,14 @@ void Client::step(float dtime)
}
/*
+ Send pending messages on out chat queue
+ */
+ if (!m_out_chat_queue.empty() && canSendChatMessage()) {
+ sendChatMessage(m_out_chat_queue.front());
+ m_out_chat_queue.pop();
+ }
+
+ /*
Handle environment
*/
// Control local player (0ms)
@@ -1143,13 +1152,50 @@ void Client::sendInventoryAction(InventoryAction *a)
Send(&pkt);
}
+bool Client::canSendChatMessage() const
+{
+ u32 now = time(NULL);
+ float time_passed = now - m_last_chat_message_sent;
+
+ float virt_chat_message_allowance = m_chat_message_allowance + time_passed *
+ (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+
+ if (virt_chat_message_allowance < 1.0f)
+ return false;
+
+ return true;
+}
+
void Client::sendChatMessage(const std::wstring &message)
{
- NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+ const s16 max_queue_size = g_settings->getS16("max_out_chat_queue_size");
+ if (canSendChatMessage()) {
+ u32 now = time(NULL);
+ float time_passed = now - m_last_chat_message_sent;
+ m_last_chat_message_sent = time(NULL);
- pkt << message;
+ m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+ if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S)
+ m_chat_message_allowance = CLIENT_CHAT_MESSAGE_LIMIT_PER_10S;
- Send(&pkt);
+ m_chat_message_allowance -= 1.0f;
+
+ NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+
+ pkt << message;
+
+ Send(&pkt);
+ } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) {
+ m_out_chat_queue.push(message);
+ } else {
+ infostream << "Could not queue chat message because maximum out chat queue size ("
+ << max_queue_size << ") is reached." << std::endl;
+ }
+}
+
+void Client::clearOutChatQueue()
+{
+ m_out_chat_queue = std::queue<std::wstring>();
}
void Client::sendChangePassword(const std::string &oldpassword,
diff --git a/src/client.h b/src/client.h
index 29cbe9c10..0255b2803 100644
--- a/src/client.h
+++ b/src/client.h
@@ -40,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream>
#include "filesys.h"
+#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
+
struct MeshMakeData;
class MapBlockMesh;
class IWritableTextureSource;
@@ -373,6 +375,7 @@ public:
const StringMap &fields);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
+ void clearOutChatQueue();
void sendChangePassword(const std::string &oldpassword,
const std::string &newpassword);
void sendDamage(u8 damage);
@@ -579,6 +582,8 @@ private:
inline std::string getPlayerName()
{ return m_env.getLocalPlayer()->getName(); }
+ bool canSendChatMessage() const;
+
float m_packetcounter_timer = 0.0f;
float m_connection_reinit_timer = 0.1f;
float m_avg_rtt_timer = 0.0f;
@@ -625,6 +630,9 @@ private:
//s32 m_daynight_i;
//u32 m_daynight_ratio;
std::queue<std::wstring> m_chat_queue;
+ std::queue<std::wstring> m_out_chat_queue;
+ u32 m_last_chat_message_sent;
+ float m_chat_message_allowance = 5.0f;
// The authentication methods we can use to enter sudo mode (=change password)
u32 m_sudo_auth_methods;
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 54043cf75..5206d19eb 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -57,6 +57,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("curl_verify_cert", "true");
settings->setDefault("enable_remote_media_server", "true");
settings->setDefault("enable_client_modding", "false");
+ settings->setDefault("max_out_chat_queue_size", "20");
// Keymap
settings->setDefault("remote_port", "30000");
diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp
index 0b957a6e2..1077d5f2d 100644
--- a/src/script/lua_api/l_client.cpp
+++ b/src/script/lua_api/l_client.cpp
@@ -85,6 +85,23 @@ int ModApiClient::l_display_chat_message(lua_State *L)
return 1;
}
+// send_chat_message(message)
+int ModApiClient::l_send_chat_message(lua_State *L)
+{
+ if (!lua_isstring(L, 1))
+ return 0;
+ std::string message = luaL_checkstring(L, 1);
+ getClient(L)->sendChatMessage(utf8_to_wide(message));
+ return 0;
+}
+
+// clear_out_chat_queue()
+int ModApiClient::l_clear_out_chat_queue(lua_State *L)
+{
+ getClient(L)->clearOutChatQueue();
+ return 0;
+}
+
// get_player_names()
int ModApiClient::l_get_player_names(lua_State *L)
{
@@ -337,6 +354,8 @@ void ModApiClient::Initialize(lua_State *L, int top)
API_FCT(get_current_modname);
API_FCT(print);
API_FCT(display_chat_message);
+ API_FCT(send_chat_message);
+ API_FCT(clear_out_chat_queue);
API_FCT(get_player_names);
API_FCT(set_last_run_mod);
API_FCT(get_last_run_mod);
diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h
index 6bb12187f..7472915f5 100644
--- a/src/script/lua_api/l_client.h
+++ b/src/script/lua_api/l_client.h
@@ -37,6 +37,12 @@ private:
// display_chat_message(message)
static int l_display_chat_message(lua_State *L);
+ // send_chat_message(message)
+ static int l_send_chat_message(lua_State *L);
+
+ // clear_out_chat_queue()
+ static int l_clear_out_chat_queue(lua_State *L);
+
// get_player_names()
static int l_get_player_names(lua_State *L);