[CSM] Add flavour limits controlled by server (#5930)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Tue, 18 Jul 2017 19:39:55 +0000 (21:39 +0200)
committerGitHub <noreply@github.com>
Tue, 18 Jul 2017 19:39:55 +0000 (21:39 +0200)
* [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

18 files changed:
builtin/settingtypes.txt
doc/client_lua_api.md
minetest.conf.example
src/client.cpp
src/client.h
src/defaultsettings.cpp
src/network/clientopcodes.cpp
src/network/clientpackethandler.cpp
src/network/networkprotocol.h
src/network/serveropcodes.cpp
src/network/serverpackethandler.cpp
src/script/lua_api/l_client.cpp
src/script/lua_api/l_client.h
src/script/lua_api/l_env.cpp
src/server.cpp
src/server.h
src/settings.cpp
src/settings.h

index c710afdcff1756298918e2920843cddb80e4cf98..60508d0dda4058ab476adabbf49a9f1b49a7727e 100644 (file)
@@ -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.
index 041c6eaa8a637b73b53484cd1280047b8596948b..1f7c6b0cce6eda69d92081d9b36d3ffbf2415e3b 100644 (file)
@@ -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"`
index d7c309c6d0630d8ac08f843096812b75219db0df..b3e28cc91660b66699b61e6b60672fb5e8f9c8ff 100644 (file)
 #    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.
index 443059c36a1c7bb01a55256522b4ba7098e23cbe..39bb1e2ff7526809a01f8f5a9d6a008068c1a00a 100644 (file)
@@ -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);
 }
 
index 0f0cd8a5eed343e895c5fc1bd480ed4e9f2eb2c0..adac83e5cbf3f190ea10a033723e1be9cbb7c51b 100644 (file)
@@ -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
index 5206d19ebd7c4ebb133d46900430f2170b59748f..84e4a314384b9ad58aab513f68f642cadafe2528 100644 (file)
@@ -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");
index f39dd6db642fcab5a554658d9e258ef31ec816c6..8ac9cee1f1ca066a69976a5e2f147e8898444210 100644 (file)
@@ -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,
index bb4db6f47b26ab607df539a7aabde0f647f93033..d06f8a94dfe9f564b9fe8300e0a77ead3a69c343 100644 (file)
@@ -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;
+}
index 7593348397ecf597c2564861779a6b859f063e94..b210d3d2e9cedd7342125a4f135ec1128b21fd4f 100644 (file)
@@ -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
index 208214369c70ff1e79d9084e35d840a3236c9de7..30357b881d5100698d52a53f170a3d660961e59b 100644 (file)
@@ -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,
index b87daba6bb01894ddc2ef8d1ef04767cefa57d6e..2151450d2f6f4d7a92e4a9bad338a04ba80315b4 100644 (file)
@@ -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,
index e7d75cce79b8134c1501193e7198e2ec3e74e69f..6f9240466994870d5d96f140de92bec5b25525d4 100644 (file)
@@ -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 = <whatever>, 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);
index 7472915f5c1b6442f5dadae918b2bf023e283570..8a3c7f0e87e6a83493e20790c88e98bc900782b9 100644 (file)
@@ -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()
index 3a4ba89f34587290cf36f3a644597c1582298536..2a57ca59bc303c60ff0d95ee8e71feae4aa06629 100644 (file)
@@ -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 <algorithm>
 #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<int>(radius, getClient(L)->getCSMNodeRangeLimit());
+       }
+#endif
+
        for (int d = start_radius; d <= radius; d++) {
                std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
                for (std::vector<v3s16>::iterator i = list.begin();
index 33384816c34d8632e454b78d76ab86e07d52be82..32e50c14f1f14a51a3693ef6c62bc26d4693dd66 100644 (file)
@@ -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,
index 112a9486371a86491a269ddc89dc1b1555c7cb7e..fff7f9b6a551ce29996c5fd01e659de8ca0269bb 100644 (file)
@@ -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<std::string, ModMetadata *> 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
-
index b4083264e1cf78a270f91637e245fd688d9638cd..1fa4ac52837aea4282702ea352acab5888c42623 100644 (file)
@@ -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));
index c4b94d67dcb82811c3c5c2d5d7cc81de82778b1f..f0baf41bfddd5aaa1184b9683e67b2aca929aaa7 100644 (file)
@@ -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;