forked from Mirrorlandia_minetest/minetest
Chat: new settings to prevent spam
Added the following chat coreside features * Chat messages length limit * Message rate limiting * Message rate kicking Note: * handleChat now takes RemotePlayer pointer instead of u16 to remove useless lookups
This commit is contained in:
parent
1079aeaa13
commit
d4c76258e3
@ -774,6 +774,15 @@ time_speed (Time speed) int 72
|
|||||||
# Interval of saving important changes in the world, stated in seconds.
|
# Interval of saving important changes in the world, stated in seconds.
|
||||||
server_map_save_interval (Map save interval) float 5.3
|
server_map_save_interval (Map save interval) float 5.3
|
||||||
|
|
||||||
|
# Set the maximum character length of a chat message sent by clients.
|
||||||
|
# chat_message_max_size int 500
|
||||||
|
|
||||||
|
# Limit a single player to send X messages per 10 seconds.
|
||||||
|
# chat_message_limit_per_10sec float 10.0
|
||||||
|
|
||||||
|
# Kick player if send more than X messages per 10 seconds.
|
||||||
|
# chat_message_limit_trigger_kick int 50
|
||||||
|
|
||||||
[**Physics]
|
[**Physics]
|
||||||
|
|
||||||
movement_acceleration_default (Default acceleration) float 3
|
movement_acceleration_default (Default acceleration) float 3
|
||||||
|
@ -933,6 +933,18 @@
|
|||||||
# type: float
|
# type: float
|
||||||
# server_map_save_interval = 5.3
|
# server_map_save_interval = 5.3
|
||||||
|
|
||||||
|
# Set the maximum character length of a chat message sent by clients. (0 to disable)
|
||||||
|
# type: integer
|
||||||
|
# chat_message_max_size = 500
|
||||||
|
|
||||||
|
# Limit a single player to send X messages per 10 seconds. (0 to disable)
|
||||||
|
# type: float
|
||||||
|
# chat_message_limit_per_10sec = 8.0
|
||||||
|
|
||||||
|
# Kick player if send more than X messages per 10 seconds. (0 to disable)
|
||||||
|
# type: integer
|
||||||
|
# chat_message_limit_trigger_kick = 50
|
||||||
|
|
||||||
### Physics
|
### Physics
|
||||||
|
|
||||||
# type: float
|
# type: float
|
||||||
@ -1523,7 +1535,7 @@
|
|||||||
# profiler.default_report_format = txt
|
# profiler.default_report_format = txt
|
||||||
|
|
||||||
# The file path relative to your worldpath in which profiles will be saved to.
|
# The file path relative to your worldpath in which profiles will be saved to.
|
||||||
#
|
#
|
||||||
# type: string
|
# type: string
|
||||||
# profiler.report_path = ""
|
# profiler.report_path = ""
|
||||||
|
|
||||||
|
@ -285,6 +285,9 @@ void set_default_settings(Settings *settings)
|
|||||||
settings->setDefault("server_unload_unused_data_timeout", "29");
|
settings->setDefault("server_unload_unused_data_timeout", "29");
|
||||||
settings->setDefault("max_objects_per_block", "49");
|
settings->setDefault("max_objects_per_block", "49");
|
||||||
settings->setDefault("server_map_save_interval", "5.3");
|
settings->setDefault("server_map_save_interval", "5.3");
|
||||||
|
settings->setDefault("chat_message_max_size", "500");
|
||||||
|
settings->setDefault("chat_message_limit_per_10sec", "8.0");
|
||||||
|
settings->setDefault("chat_message_limit_trigger_kick", "50");
|
||||||
settings->setDefault("sqlite_synchronous", "2");
|
settings->setDefault("sqlite_synchronous", "2");
|
||||||
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
|
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
|
||||||
settings->setDefault("dedicated_server_step", "0.1");
|
settings->setDefault("dedicated_server_step", "0.1");
|
||||||
|
@ -1065,7 +1065,7 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
|
|||||||
std::wstring wname = narrow_to_wide(name);
|
std::wstring wname = narrow_to_wide(name);
|
||||||
|
|
||||||
std::wstring answer_to_sender = handleChat(name, wname, message,
|
std::wstring answer_to_sender = handleChat(name, wname, message,
|
||||||
true, pkt->getPeerId());
|
true, dynamic_cast<RemotePlayer *>(player));
|
||||||
if (!answer_to_sender.empty()) {
|
if (!answer_to_sender.empty()) {
|
||||||
// Send the answer to sender
|
// Send the answer to sender
|
||||||
SendChatMessage(pkt->getPeerId(), answer_to_sender);
|
SendChatMessage(pkt->getPeerId(), answer_to_sender);
|
||||||
@ -1656,16 +1656,16 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // action == 4
|
} // action == 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
5: rightclick air
|
5: rightclick air
|
||||||
*/
|
*/
|
||||||
else if (action == 5) {
|
else if (action == 5) {
|
||||||
ItemStack item = playersao->getWieldedItem();
|
ItemStack item = playersao->getWieldedItem();
|
||||||
|
|
||||||
actionstream << player->getName() << " activates "
|
actionstream << player->getName() << " activates "
|
||||||
<< item.name << std::endl;
|
<< item.name << std::endl;
|
||||||
|
|
||||||
if (m_script->item_OnSecondaryUse(
|
if (m_script->item_OnSecondaryUse(
|
||||||
item, playersao)) {
|
item, playersao)) {
|
||||||
if( playersao->setWieldedItem(item)) {
|
if( playersao->setWieldedItem(item)) {
|
||||||
|
@ -227,10 +227,25 @@ void Player::clearHud()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static config cache for remoteplayer
|
||||||
|
bool RemotePlayer::m_setting_cache_loaded = false;
|
||||||
|
float RemotePlayer::m_setting_chat_message_limit_per_10sec = 0.0f;
|
||||||
|
u16 RemotePlayer::m_setting_chat_message_limit_trigger_kick = 0;
|
||||||
|
|
||||||
RemotePlayer::RemotePlayer(IGameDef *gamedef, const char *name):
|
RemotePlayer::RemotePlayer(IGameDef *gamedef, const char *name):
|
||||||
Player(gamedef, name),
|
Player(gamedef, name),
|
||||||
m_sao(NULL)
|
m_sao(NULL),
|
||||||
|
m_last_chat_message_sent(time(NULL)),
|
||||||
|
m_chat_message_allowance(5.0f),
|
||||||
|
m_message_rate_overhead(0)
|
||||||
{
|
{
|
||||||
|
if (!RemotePlayer::m_setting_cache_loaded) {
|
||||||
|
RemotePlayer::m_setting_chat_message_limit_per_10sec =
|
||||||
|
g_settings->getFloat("chat_message_limit_per_10sec");
|
||||||
|
RemotePlayer::m_setting_chat_message_limit_trigger_kick =
|
||||||
|
g_settings->getU16("chat_message_limit_trigger_kick");
|
||||||
|
RemotePlayer::m_setting_cache_loaded = true;
|
||||||
|
}
|
||||||
movement_acceleration_default = g_settings->getFloat("movement_acceleration_default") * BS;
|
movement_acceleration_default = g_settings->getFloat("movement_acceleration_default") * BS;
|
||||||
movement_acceleration_air = g_settings->getFloat("movement_acceleration_air") * BS;
|
movement_acceleration_air = g_settings->getFloat("movement_acceleration_air") * BS;
|
||||||
movement_acceleration_fast = g_settings->getFloat("movement_acceleration_fast") * BS;
|
movement_acceleration_fast = g_settings->getFloat("movement_acceleration_fast") * BS;
|
||||||
@ -304,3 +319,42 @@ void RemotePlayer::setPosition(const v3f &position)
|
|||||||
m_sao->setBasePosition(position);
|
m_sao->setBasePosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RemotePlayerChatResult RemotePlayer::canSendChatMessage()
|
||||||
|
{
|
||||||
|
// Rate limit messages
|
||||||
|
u32 now = time(NULL);
|
||||||
|
float time_passed = now - m_last_chat_message_sent;
|
||||||
|
m_last_chat_message_sent = now;
|
||||||
|
|
||||||
|
// If this feature is disabled
|
||||||
|
if (m_setting_chat_message_limit_per_10sec <= 0.0) {
|
||||||
|
return RPLAYER_CHATRESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_chat_message_allowance += time_passed * (m_setting_chat_message_limit_per_10sec / 8.0f);
|
||||||
|
if (m_chat_message_allowance > m_setting_chat_message_limit_per_10sec) {
|
||||||
|
m_chat_message_allowance = m_setting_chat_message_limit_per_10sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_chat_message_allowance < 1.0f) {
|
||||||
|
infostream << "Player " << m_name
|
||||||
|
<< " chat limited due to excessive message amount." << std::endl;
|
||||||
|
|
||||||
|
// Kick player if flooding is too intensive
|
||||||
|
m_message_rate_overhead++;
|
||||||
|
if (m_message_rate_overhead > RemotePlayer::m_setting_chat_message_limit_trigger_kick) {
|
||||||
|
return RPLAYER_CHATRESULT_KICK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RPLAYER_CHATRESULT_FLOODING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinit message overhead
|
||||||
|
if (m_message_rate_overhead > 0) {
|
||||||
|
m_message_rate_overhead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_chat_message_allowance -= 1.0f;
|
||||||
|
return RPLAYER_CHATRESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
16
src/player.h
16
src/player.h
@ -439,7 +439,11 @@ private:
|
|||||||
Mutex m_mutex;
|
Mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum RemotePlayerChatResult {
|
||||||
|
RPLAYER_CHATRESULT_OK,
|
||||||
|
RPLAYER_CHATRESULT_FLOODING,
|
||||||
|
RPLAYER_CHATRESULT_KICK,
|
||||||
|
};
|
||||||
/*
|
/*
|
||||||
Player on the server
|
Player on the server
|
||||||
*/
|
*/
|
||||||
@ -457,8 +461,18 @@ public:
|
|||||||
{ m_sao = sao; }
|
{ m_sao = sao; }
|
||||||
void setPosition(const v3f &position);
|
void setPosition(const v3f &position);
|
||||||
|
|
||||||
|
const RemotePlayerChatResult canSendChatMessage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PlayerSAO *m_sao;
|
PlayerSAO *m_sao;
|
||||||
|
|
||||||
|
static bool m_setting_cache_loaded;
|
||||||
|
static float m_setting_chat_message_limit_per_10sec;
|
||||||
|
static u16 m_setting_chat_message_limit_trigger_kick;
|
||||||
|
|
||||||
|
u32 m_last_chat_message_sent;
|
||||||
|
float m_chat_message_allowance;
|
||||||
|
u16 m_message_rate_overhead;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -358,6 +358,7 @@ Server::Server(
|
|||||||
add_legacy_abms(m_env, m_nodedef);
|
add_legacy_abms(m_env, m_nodedef);
|
||||||
|
|
||||||
m_liquid_transform_every = g_settings->getFloat("liquid_update");
|
m_liquid_transform_every = g_settings->getFloat("liquid_update");
|
||||||
|
m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server()
|
Server::~Server()
|
||||||
@ -2734,8 +2735,7 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
|
std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
|
||||||
const std::wstring &wmessage, bool check_shout_priv,
|
const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
|
||||||
u16 peer_id_to_avoid_sending)
|
|
||||||
{
|
{
|
||||||
// If something goes wrong, this player is to blame
|
// If something goes wrong, this player is to blame
|
||||||
RollbackScopeActor rollback_scope(m_rollback,
|
RollbackScopeActor rollback_scope(m_rollback,
|
||||||
@ -2753,6 +2753,26 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
|
|||||||
if (ate)
|
if (ate)
|
||||||
return L"";
|
return L"";
|
||||||
|
|
||||||
|
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")
|
||||||
|
<< " messages per 10 seconds.";
|
||||||
|
return ws.str();
|
||||||
|
}
|
||||||
|
case RPLAYER_CHATRESULT_KICK:
|
||||||
|
DenyAccess_Legacy(player->peer_id, L"You have been kicked due to message flooding.");
|
||||||
|
return L"";
|
||||||
|
case RPLAYER_CHATRESULT_OK: break;
|
||||||
|
default: FATAL_ERROR("Unhandled chat filtering result found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_max_chatmessage_length > 0 && wmessage.length() > m_max_chatmessage_length) {
|
||||||
|
return L"Your message exceed the maximum chat message limit set on the server. "
|
||||||
|
"It was refused. Send a shorter message";
|
||||||
|
}
|
||||||
|
|
||||||
// Commands are implemented in Lua, so only catch invalid
|
// Commands are implemented in Lua, so only catch invalid
|
||||||
// commands that were not "eaten" and send an error back
|
// commands that were not "eaten" and send an error back
|
||||||
if (wmessage[0] == L'/') {
|
if (wmessage[0] == L'/') {
|
||||||
@ -2787,6 +2807,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
|
|||||||
|
|
||||||
std::vector<u16> clients = m_clients.getClientIDs();
|
std::vector<u16> clients = m_clients.getClientIDs();
|
||||||
|
|
||||||
|
u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
|
||||||
for (u16 i = 0; i < clients.size(); i++) {
|
for (u16 i = 0; i < clients.size(); i++) {
|
||||||
u16 cid = clients[i];
|
u16 cid = clients[i];
|
||||||
if (cid != peer_id_to_avoid_sending)
|
if (cid != peer_id_to_avoid_sending)
|
||||||
|
@ -487,7 +487,7 @@ private:
|
|||||||
std::wstring handleChat(const std::string &name, const std::wstring &wname,
|
std::wstring handleChat(const std::string &name, const std::wstring &wname,
|
||||||
const std::wstring &wmessage,
|
const std::wstring &wmessage,
|
||||||
bool check_shout_priv = false,
|
bool check_shout_priv = false,
|
||||||
u16 peer_id_to_avoid_sending = PEER_ID_INEXISTENT);
|
RemotePlayer *player = NULL);
|
||||||
void handleAdminChat(const ChatEventChat *evt);
|
void handleAdminChat(const ChatEventChat *evt);
|
||||||
|
|
||||||
v3f findSpawnPos();
|
v3f findSpawnPos();
|
||||||
@ -522,6 +522,7 @@ private:
|
|||||||
// If true, do not allow multiple players and hide some multiplayer
|
// If true, do not allow multiple players and hide some multiplayer
|
||||||
// functionality
|
// functionality
|
||||||
bool m_simple_singleplayer_mode;
|
bool m_simple_singleplayer_mode;
|
||||||
|
u16 m_max_chatmessage_length;
|
||||||
|
|
||||||
// Thread can set; step() will throw as ServerError
|
// Thread can set; step() will throw as ServerError
|
||||||
MutexedVariable<std::string> m_async_fatal_error;
|
MutexedVariable<std::string> m_async_fatal_error;
|
||||||
|
Loading…
Reference in New Issue
Block a user