From: Perttu Ahola Date: Sat, 21 Jul 2012 11:38:49 +0000 (+0300) Subject: Server-side checking of digging; disable_anticheat setting X-Git-Url: http://81.2.79.47:8989/gitweb/?a=commitdiff_plain;h=2795f44f0316c83728bc8059a020869058498f78;p=zefram%2Fminetest%2Fminetest_engine.git Server-side checking of digging; disable_anticheat setting --- diff --git a/minetest.conf.example b/minetest.conf.example index ef1b8d26..fdfbf201 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -158,6 +158,8 @@ #static_spawnpoint = 0, 10, 0 # If true, new players cannot join with an empty password #disallow_empty_password = false +# If true, disable cheat prevention in multiplayer +#disable_anticheat = false # Profiler data print interval. #0 = disable. #profiler_print_interval = 0 diff --git a/src/content_sao.cpp b/src/content_sao.cpp index e9b5782a..0488c802 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -764,6 +764,8 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, m_last_good_position(0,0,0), m_last_good_position_age(0), m_time_from_last_punch(0), + m_nocheat_dig_pos(32767, 32767, 32767), + m_nocheat_dig_time(0), m_wield_index(0), m_position_not_sent(false), m_armor_groups_sent(false), @@ -874,8 +876,9 @@ void PlayerSAO::step(float dtime, bool send_recommended) } m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; - if(m_is_singleplayer) + if(m_is_singleplayer || g_settings->getBool("disable_anticheat")) { m_last_good_position = m_player->getPosition(); m_last_good_position_age = 0; @@ -888,7 +891,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) NOTE: Actually the server should handle player physics like the client does and compare player's position to what is calculated on our side. This is required when eg. players fly due to an - explosion. + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. */ float player_max_speed = 0; diff --git a/src/content_sao.h b/src/content_sao.h index 6efb9e3e..fac16ca9 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -173,6 +173,9 @@ public: { return m_peer_id; } + + // Cheat prevention + v3f getLastGoodPosition() const { return m_last_good_position; @@ -183,6 +186,26 @@ public: m_time_from_last_punch = 0.0; return r; } + void noCheatDigStart(v3s16 p) + { + m_nocheat_dig_pos = p; + m_nocheat_dig_time = 0; + } + v3s16 getNoCheatDigPos() + { + return m_nocheat_dig_pos; + } + float getNoCheatDigTime() + { + return m_nocheat_dig_time; + } + void noCheatDigEnd() + { + m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + } + + // Other + void updatePrivileges(const std::set &privs, bool is_singleplayer) { @@ -196,9 +219,14 @@ private: Player *m_player; u16 m_peer_id; Inventory *m_inventory; + + // Cheat prevention v3f m_last_good_position; float m_last_good_position_age; float m_time_from_last_punch; + v3s16 m_nocheat_dig_pos; + float m_nocheat_dig_time; + int m_wield_index; bool m_position_not_sent; ItemGroupList m_armor_groups; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a9c0de6a..dabdbb4a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -121,6 +121,7 @@ void set_default_settings(Settings *settings) settings->setDefault("unlimited_player_transfer_distance", "true"); settings->setDefault("enable_pvp", "true"); settings->setDefault("disallow_empty_password", "false"); + settings->setDefault("disable_anticheat", "false"); settings->setDefault("profiler_print_interval", "0"); settings->setDefault("enable_mapgen_debug_info", "false"); diff --git a/src/game.cpp b/src/game.cpp index b29d2d64..e2e6dd8c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2112,7 +2112,9 @@ void the_game( ldown_for_dig = true; } MapNode n = client.getEnv().getClientMap().getNode(nodepos); - + + // NOTE: Similar piece of code exists on the server side for + // cheat detection. // Get digging parameters DigParams params = getDigParams(nodedef->get(n).groups, &playeritem_toolcap); @@ -2160,7 +2162,7 @@ void the_game( { dig_index = crack_animation_length; } - + // Don't show cracks if not diggable if(dig_time_complete >= 100000.0) { diff --git a/src/server.cpp b/src/server.cpp index 80df9fc7..a44a6a2e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3014,6 +3014,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } if(n.getContent() != CONTENT_IGNORE) scriptapi_node_on_punch(m_lua, p_under, n, playersao); + // Cheat prevention + playersao->noCheatDigStart(p_under); } else if(pointed.type == POINTEDTHING_OBJECT) { @@ -3051,7 +3053,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ else if(action == 2) { - // Only complete digging of nodes + // Only digging of nodes if(pointed.type == POINTEDTHING_NODE) { MapNode n(CONTENT_IGNORE); @@ -3067,10 +3069,65 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_emerge_queue.addBlock(peer_id, getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); } - if(n.getContent() != CONTENT_IGNORE) + + /* Cheat prevention */ + bool is_valid_dig = true; + if(!isSingleplayer() && !g_settings->getBool("disable_anticheat")) + { + v3s16 nocheat_p = playersao->getNoCheatDigPos(); + float nocheat_t = playersao->getNoCheatDigTime(); + playersao->noCheatDigEnd(); + // If player didn't start digging this, ignore dig + if(nocheat_p != p_under){ + infostream<<"Server: NoCheat: "<getName() + <<" started digging " + <getInventory()->getList("main"); + if(mlist != NULL) + playeritem = mlist->getItem(playersao->getWieldIndex()); + ToolCapabilities playeritem_toolcap = + playeritem.getToolCapabilities(m_itemdef); + // Get diggability and expected digging time + DigParams params = getDigParams(m_nodedef->get(n).groups, + &playeritem_toolcap); + // If can't dig, try hand + if(!params.diggable){ + const ItemDefinition &hand = m_itemdef->get(""); + const ToolCapabilities *tp = hand.tool_capabilities; + if(tp) + params = getDigParams(m_nodedef->get(n).groups, tp); + } + // If can't dig, ignore dig + if(!params.diggable){ + infostream<<"Server: NoCheat: "<getName() + <<" completed digging "< 0.3 && nocheat_t < 0.5 * params.time){ + infostream<<"Server: NoCheat: "<getName() + <<" completed digging " + <getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) + // Send unusual result (that is, node not being removed) + if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) { // Re-send block to revert change on client-side RemoteClient *client = getClient(peer_id);