From 79f19b8369a0120fbf0bd0ee3739989fbbbca50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Tue, 18 Jul 2017 21:39:55 +0200 Subject: [PATCH] [CSM] Add flavour limits controlled by server (#5930) * [CSM] Add flavour limits controlled by server Server send flavour limits to client permitting to disable or limit some Lua calls * Add limits for reading nodedefs and itemdefs * flavour: Add lookup node limits * Merge get_node_or_nil into get_node. Sending fake node doesn't make sense in CSM, just return nil if node is not available for any reason * Add node range customization when noderange flavour is enabled (default 8 nodes) * Limit nodes range & disable chat message sending by default * Bump protocol version --- builtin/settingtypes.txt | 13 +++++++++++++ doc/client_lua_api.md | 8 +++----- minetest.conf.example | 13 +++++++++++++ src/client.cpp | 14 ++++++++++++++ src/client.h | 23 +++++++++++++++++++++++ src/defaultsettings.cpp | 2 ++ src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 5 +++++ src/network/networkprotocol.h | 19 ++++++++++++++++--- src/network/serveropcodes.cpp | 2 +- src/network/serverpackethandler.cpp | 2 ++ src/script/lua_api/l_client.cpp | 27 ++++++++++++--------------- src/script/lua_api/l_client.h | 3 --- src/script/lua_api/l_env.cpp | 10 ++++++++++ src/server.cpp | 9 +++++++++ src/server.h | 7 ++++++- src/settings.cpp | 5 +++++ src/settings.h | 1 + 18 files changed, 136 insertions(+), 29 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index c710afdcf..60508d0dd 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -960,6 +960,19 @@ block_send_optimize_distance (block send optimize distance) int 4 2 # so that the utility of noclip mode is reduced. server_side_occlusion_culling (Server side occlusion culling) bool true +# Restricts the access of certain client-side functions on servers +# Combine these byteflags below to restrict more client-side features: +# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit) +# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) +# READ_ITEMDEFS: 4 (disable get_item_def call client-side) +# READ_NODEDEFS: 8 (disable get_node_def call client-side) +# type: int +csm_flavour_limits (Client side modding flavour limits) int 3 + +# If the CSM flavour for node range is enabled, get_node is limited to +# this many nodes from the player. +csm_flavour_noderange_limit (Client side noderange flavour limit) int 8 + [*Mapgen] # Name of map generator to be used when creating a new world. diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 041c6eaa8..1f7c6b0cc 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -701,12 +701,10 @@ Call these functions only at load time! * Returns the time of day: `0` for midnight, `0.5` for midday ### Map -* `minetest.get_node(pos)` - * Returns the node at the given position as table in the format - `{name="node_name", param1=0, param2=0}`, returns `{name="ignore", param1=0, param2=0}` - for unloaded areas. * `minetest.get_node_or_nil(pos)` - * Same as `get_node` but returns `nil` for unloaded areas. + * Returns the node at the given position as table in the format + `{name="node_name", param1=0, param2=0}`, returns `nil` + for unloaded areas or flavour limited areas. * `minetest.find_node_near(pos, radius, nodenames, [search_center])`: returns pos or `nil` * `radius`: using a maximum metric * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` diff --git a/minetest.conf.example b/minetest.conf.example index d7c309c6d..b3e28cc91 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1172,6 +1172,19 @@ # type: bool # server_side_occlusion_culling = true +# Restricts the access of certain client-side functions on servers +# Combine these byteflags below to restrict more client-side features: +# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit) +# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) +# READ_ITEMDEFS: 4 (disable get_item_def call client-side) +# READ_NODEDEFS: 8 (disable get_node_def call client-side) +# type: int +# csm_flavour_limits = 3 + +# If the CSM flavour for node range is enabled, get_node is limited to +# this many nodes from the player. +# csm_flavour_noderange_limit 8 + ## Mapgen # Name of map generator to be used when creating a new world. diff --git a/src/client.cpp b/src/client.cpp index 443059c36..39bb1e2ff 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1361,8 +1361,22 @@ void Client::removeNode(v3s16 p) } } +/** + * Helper function for Client Side Modding + * Flavour is applied there, this should not be used for core engine + * @param p + * @param is_valid_position + * @return + */ MapNode Client::getNode(v3s16 p, bool *is_valid_position) { + if (checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOOKUP_NODES)) { + v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); + if ((u32) ppos.getDistanceFrom(p) > m_csm_noderange_limit) { + *is_valid_position = false; + return MapNode(); + } + } return m_env.getMap().getNodeNoEx(p, is_valid_position); } diff --git a/src/client.h b/src/client.h index 0f0cd8a5e..adac83e5c 100644 --- a/src/client.h +++ b/src/client.h @@ -364,6 +364,7 @@ public: void handleCommand_EyeOffset(NetworkPacket* pkt); void handleCommand_UpdatePlayerList(NetworkPacket* pkt); void handleCommand_SrpBytesSandB(NetworkPacket* pkt); + void handleCommand_CSMFlavourLimits(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -396,6 +397,14 @@ public: // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); + + /** + * Helper function for Client Side Modding + * Flavour is applied there, this should not be used for core engine + * @param p + * @param is_valid_position + * @return + */ MapNode getNode(v3s16 p, bool *is_valid_position); void addNode(v3s16 p, MapNode n, bool remove_metadata = true); @@ -552,6 +561,16 @@ public: return m_address_name; } + inline bool checkCSMFlavourLimit(CSMFlavourLimit flag) const + { + return m_csm_flavour_limits & flag; + } + + u32 getCSMNodeRangeLimit() const + { + return m_csm_noderange_limit; + } + private: // Virtual methods from con::PeerHandler @@ -705,6 +724,10 @@ private: GameUIFlags *m_game_ui_flags; bool m_shutdown = false; + + // CSM flavour limits byteflag + u64 m_csm_flavour_limits = CSMFlavourLimit::CSM_FL_NONE; + u32 m_csm_noderange_limit = 8; }; #endif // !CLIENT_HEADER diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 5206d19eb..84e4a3143 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -294,6 +294,8 @@ void set_default_settings(Settings *settings) settings->setDefault("max_block_send_distance", "9"); settings->setDefault("block_send_optimize_distance", "4"); settings->setDefault("server_side_occlusion_culling", "true"); + settings->setDefault("csm_flavour_limits", "3"); + settings->setDefault("csm_flavour_noderange_limit", "8"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); settings->setDefault("time_speed", "72"); settings->setDefault("server_unload_unused_data_timeout", "29"); diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index f39dd6db6..8ac9cee1f 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -66,7 +66,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Inventory }, // 0x27 null_command_handler, { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29 - null_command_handler, + { "TOCLIENT_CSM_FLAVOUR_LIMITS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMFlavourLimits }, // 0x2A null_command_handler, null_command_handler, null_command_handler, diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index bb4db6f47..d06f8a94d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1376,3 +1376,8 @@ void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt) resp_pkt << std::string(bytes_M, len_M); Send(&resp_pkt); } + +void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt) +{ + *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit; +} diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 759334839..b210d3d2e 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef NETWORKPROTOCOL_HEADER -#define NETWORKPROTOCOL_HEADER +#pragma once + #include "util/string.h" /* @@ -168,6 +168,7 @@ with this program; if not, write to the Free Software Foundation, Inc., * sender * type (RAW, NORMAL, ANNOUNCE, SYSTEM) * content + Add TOCLIENT_CSM_FLAVOUR_LIMITS to define which CSM flavour should be limited */ #define LATEST_PROTOCOL_VERSION 35 @@ -313,6 +314,11 @@ enum ToClientCommand f1000 time_speed */ + TOCLIENT_CSM_FLAVOUR_LIMITS = 0x2A, + /* + u32 CSMFlavourLimits byteflag + */ + // (oops, there is some gap here) TOCLIENT_CHAT_MESSAGE = 0x2F, @@ -1003,5 +1009,12 @@ enum PlayerListModifer: u8 PLAYER_LIST_REMOVE, }; +enum CSMFlavourLimit : u64 { + CSM_FL_NONE = 0x00000000, + CSM_FL_LOOKUP_NODES = 0x00000001, // Limit node lookups + CSM_FL_CHAT_MESSAGES = 0x00000002, // Disable chat message sending from CSM + CSM_FL_READ_ITEMDEFS = 0x00000004, // Disable itemdef lookups + CSM_FL_READ_NODEDEFS = 0x00000008, // Disable nodedef lookups + CSM_FL_ALL = 0xFFFFFFFF, +}; -#endif diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 208214369..30357b881 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -155,7 +155,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_INVENTORY", 0, true }, // 0x27 null_command_factory, { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 - null_command_factory, + { "TOCLIENT_CSM_FLAVOUR_LIMITS", 0, true }, // 0x2A null_command_factory, null_command_factory, null_command_factory, diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index b87daba6b..2151450d2 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -643,6 +643,8 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) float time_speed = g_settings->getFloat("time_speed"); SendTimeOfDay(pkt->getPeerId(), time, time_speed); + SendCSMFlavourLimits(pkt->getPeerId()); + // Warnings about protocol version can be issued here if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) { SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index e7d75cce7..6f9240466 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -91,6 +91,11 @@ int ModApiClient::l_send_chat_message(lua_State *L) { if (!lua_isstring(L, 1)) return 0; + + // If server disabled this API, discard + if (getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_CHAT_MESSAGES)) + return 0; + std::string message = luaL_checkstring(L, 1); getClient(L)->sendChatMessage(utf8_to_wide(message)); return 0; @@ -166,24 +171,11 @@ int ModApiClient::l_gettext(lua_State *L) // get_node(pos) // pos = {x=num, y=num, z=num} -int ModApiClient::l_get_node(lua_State *L) -{ - // pos - v3s16 pos = read_v3s16(L, 1); - // Do it - bool pos_ok; - MapNode n = getClient(L)->getNode(pos, &pos_ok); - // Return node - pushnode(L, n, getClient(L)->ndef()); - return 1; -} - -// get_node_or_nil(pos) -// pos = {x=num, y=num, z=num} int ModApiClient::l_get_node_or_nil(lua_State *L) { // pos v3s16 pos = read_v3s16(L, 1); + // Do it bool pos_ok; MapNode n = getClient(L)->getNode(pos, &pos_ok); @@ -290,6 +282,9 @@ int ModApiClient::l_get_item_def(lua_State *L) IItemDefManager *idef = gdef->idef(); assert(idef); + if (getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_READ_ITEMDEFS)) + return 0; + if (!lua_isstring(L, 1)) return 0; @@ -315,6 +310,9 @@ int ModApiClient::l_get_node_def(lua_State *L) if (!lua_isstring(L, 1)) return 0; + if (getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_READ_NODEDEFS)) + return 0; + const std::string &name = lua_tostring(L, 1); const ContentFeatures &cf = ndef->get(ndef->getId(name)); if (cf.name != name) // Unknown node. | name = , cf.name = ignore @@ -363,7 +361,6 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(show_formspec); API_FCT(send_respawn); API_FCT(gettext); - API_FCT(get_node); API_FCT(get_node_or_nil); API_FCT(get_wielded_item); API_FCT(disconnect); diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 7472915f5..8a3c7f0e8 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -65,9 +65,6 @@ private: static int l_set_last_run_mod(lua_State *L); // get_node(pos) - static int l_get_node(lua_State *L); - - // get_node_or_nil(pos) static int l_get_node_or_nil(lua_State *L); // get_wielded_item() diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 3a4ba89f3..2a57ca59b 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" +#include #include "scripting_server.h" #include "environment.h" #include "server.h" @@ -726,6 +727,15 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) } int start_radius = (lua_toboolean(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (getClient(L) && + getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOOKUP_NODES)) { + radius = std::max(radius, getClient(L)->getCSMNodeRangeLimit()); + } +#endif + for (int d = start_radius; d <= radius; d++) { std::vector list = FacePositionCache::getFacePositions(d); for (std::vector::iterator i = list.begin(); diff --git a/src/server.cpp b/src/server.cpp index 33384816c..32e50c14f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -298,6 +298,8 @@ Server::Server( m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); + m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits"); + m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit"); } Server::~Server() @@ -2017,7 +2019,14 @@ void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, boo m_clients.send(pkt.getPeerId(), reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1, &pkt, reliable); +} +void Server::SendCSMFlavourLimits(u16 peer_id) +{ + NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS, + sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id); + pkt << m_csm_flavour_limits << m_csm_noderange_limit; + Send(&pkt); } s32 Server::playSound(const SimpleSoundSpec &spec, diff --git a/src/server.h b/src/server.h index 112a94863..fff7f9b6a 100644 --- a/src/server.h +++ b/src/server.h @@ -466,6 +466,8 @@ private: u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); + void SendCSMFlavourLimits(u16 peer_id); + /* Something random */ @@ -664,6 +666,10 @@ private: std::unordered_map m_mod_storages; float m_mod_storage_save_timer = 10.0f; + + // CSM flavour limits byteflag + u64 m_csm_flavour_limits = CSMFlavourLimit::CSM_FL_NONE; + u32 m_csm_noderange_limit = 8; }; /* @@ -674,4 +680,3 @@ private: void dedicated_server_loop(Server &server, bool &kill); #endif - diff --git a/src/settings.cpp b/src/settings.cpp index b4083264e..1fa4ac528 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -398,6 +398,11 @@ s16 Settings::getS16(const std::string &name) const } +u32 Settings::getU32(const std::string &name) const +{ + return (u32) stoi(get(name)); +} + s32 Settings::getS32(const std::string &name) const { return stoi(get(name)); diff --git a/src/settings.h b/src/settings.h index c4b94d67d..f0baf41bf 100644 --- a/src/settings.h +++ b/src/settings.h @@ -134,6 +134,7 @@ public: bool getBool(const std::string &name) const; u16 getU16(const std::string &name) const; s16 getS16(const std::string &name) const; + u32 getU32(const std::string &name) const; s32 getS32(const std::string &name) const; u64 getU64(const std::string &name) const; float getFloat(const std::string &name) const;