# 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.
* 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"`
# 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.
}
}
+/**
+ * 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);
}
void handleCommand_EyeOffset(NetworkPacket* pkt);
void handleCommand_UpdatePlayerList(NetworkPacket* pkt);
void handleCommand_SrpBytesSandB(NetworkPacket* pkt);
+ void handleCommand_CSMFlavourLimits(NetworkPacket *pkt);
void ProcessData(NetworkPacket *pkt);
// 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);
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
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
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");
{ "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,
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;
+}
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef NETWORKPROTOCOL_HEADER
-#define NETWORKPROTOCOL_HEADER
+#pragma once
+
#include "util/string.h"
/*
* 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
f1000 time_speed
*/
+ TOCLIENT_CSM_FLAVOUR_LIMITS = 0x2A,
+ /*
+ u32 CSMFlavourLimits byteflag
+ */
+
// (oops, there is some gap here)
TOCLIENT_CHAT_MESSAGE = 0x2F,
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
{ "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,
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,
{
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;
// 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);
IItemDefManager *idef = gdef->idef();
assert(idef);
+ if (getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_READ_ITEMDEFS))
+ return 0;
+
if (!lua_isstring(L, 1))
return 0;
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
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);
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()
#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"
}
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();
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()
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,
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
*/
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;
};
/*
void dedicated_server_loop(Server &server, bool &kill);
#endif
-
}
+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));
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;