Breath cheat fix: server side 4978/head
authorLoic Blot <loic.blot@unix-experience.fr>
Sun, 1 Jan 2017 15:13:01 +0000 (16:13 +0100)
committerLoic Blot <loic.blot@unix-experience.fr>
Sun, 1 Jan 2017 22:11:26 +0000 (23:11 +0100)
Breath is now handled server side. Changing this behaviour required some modifications to core:

* Ignore TOSERVER_BREATH package, marking it as obsolete
* Clients doesn't send the breath to server anymore
* Use PlayerSAO pointer instead of peer_id in Server::SendPlayerBreath to prevent a useless lookup (little perf gain)
* drop a useless static_cast in emergePlayer

13 files changed:
src/client.cpp
src/content_sao.cpp
src/content_sao.h
src/environment.cpp
src/network/clientopcodes.cpp
src/network/networkprotocol.h
src/network/serveropcodes.cpp
src/network/serverpackethandler.cpp
src/remoteplayer.cpp
src/script/lua_api/l_object.cpp
src/server.cpp
src/server.h
src/unittest/test_player.cpp

index 5476aad0e2ebdff8748ee3093a05dd94b6eb32bc..1446ebad8f957bb53f7d18040067007fdaf0366c 100644 (file)
@@ -499,9 +499,10 @@ void Client::step(float dtime)
                                m_client_event_queue.push(event);
                        }
                }
-               else if(event.type == CEE_PLAYER_BREATH) {
-                               u16 breath = event.player_breath.amount;
-                               sendBreath(breath);
+               // Protocol v29 or greater obsoleted this event
+               else if (event.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
+                       u16 breath = event.player_breath.amount;
+                       sendBreath(breath);
                }
        }
 
@@ -1270,6 +1271,10 @@ void Client::sendBreath(u16 breath)
 {
        DSTACK(FUNCTION_NAME);
 
+       // Protocol v29 make this obsolete
+       if (m_proto_ver >= 29)
+               return;
+
        NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
        pkt << breath;
        Send(&pkt);
index 77ab51a02694afa919393e1e4f4d125841830a72..f866d43721cda17e8c7cfc1a41e3597e853b6503 100644 (file)
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h" // For compressZlib
 #include "tool.h" // For ToolCapabilities
 #include "gamedef.h"
+#include "nodedef.h"
 #include "remoteplayer.h"
 #include "server.h"
 #include "scripting_game.h"
@@ -940,8 +941,35 @@ bool PlayerSAO::isAttached()
 
 void PlayerSAO::step(float dtime, bool send_recommended)
 {
-       if(!m_properties_sent)
-       {
+       if (m_drowning_interval.step(dtime, 2.0)) {
+               // get head position
+               v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
+               MapNode n = m_env->getMap().getNodeNoEx(p);
+               const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n);
+               // If node generates drown
+               if (c.drowning > 0) {
+                       if (m_hp > 0 && m_breath > 0)
+                               setBreath(m_breath - 1);
+
+                       // No more breath, damage player
+                       if (m_breath == 0) {
+                               setHP(m_hp - c.drowning);
+                               ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this);
+                       }
+               }
+       }
+
+       if (m_breathing_interval.step(dtime, 0.5)) {
+               // get head position
+               v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
+               MapNode n = m_env->getMap().getNodeNoEx(p);
+               const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n);
+               // If player is alive & no drowning, breath
+               if (m_hp > 0 && c.drowning == 0)
+                       setBreath(m_breath + 1);
+       }
+
+       if (!m_properties_sent) {
                m_properties_sent = true;
                std::string str = getPropertyPacket();
                // create message and add to list
@@ -1237,12 +1265,15 @@ void PlayerSAO::setHP(s16 hp)
                m_properties_sent = false;
 }
 
-void PlayerSAO::setBreath(const u16 breath)
+void PlayerSAO::setBreath(const u16 breath, bool send)
 {
        if (m_player && breath != m_breath)
                m_player->setDirty(true);
 
-       m_breath = breath;
+       m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
+
+       if (send)
+               ((Server *) m_env->getGameDef())->SendPlayerBreath(this);
 }
 
 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
index 86255183d50e15fa80e34db4cf87d30a7f981088..9c66068b308e3fdadf7683818025d00dce8da13b 100644 (file)
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef CONTENT_SAO_HEADER
 #define CONTENT_SAO_HEADER
 
+#include <util/numeric.h>
 #include "serverobject.h"
 #include "itemgroup.h"
 #include "object_properties.h"
@@ -232,7 +233,7 @@ public:
        void setHPRaw(s16 hp) { m_hp = hp; }
        s16 readDamage();
        u16 getBreath() const { return m_breath; }
-       void setBreath(const u16 breath);
+       void setBreath(const u16 breath, bool send = true);
        void setArmorGroups(const ItemGroupList &armor_groups);
        ItemGroupList getArmorGroups();
        void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop);
@@ -339,6 +340,10 @@ private:
        v3s16 m_nocheat_dig_pos;
        float m_nocheat_dig_time;
 
+       // Timers
+       IntervalLimiter m_breathing_interval;
+       IntervalLimiter m_drowning_interval;
+
        int m_wield_index;
        bool m_position_not_sent;
        ItemGroupList m_armor_groups;
index 707d89659bca7967f28620036a4eb8fb487ccc4c..ac9b5b079ae72d6f20783c07a99d0273a9bcf74c 100644 (file)
@@ -2511,51 +2511,51 @@ void ClientEnvironment::step(float dtime)
                }
        }
 
-       /*
-               Drowning
-       */
-       if(m_drowning_interval.step(dtime, 2.0))
-       {
-               v3f pf = lplayer->getPosition();
-
-               // head
-               v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
-               MapNode n = m_map->getNodeNoEx(p);
-               ContentFeatures c = m_gamedef->ndef()->get(n);
-               u8 drowning_damage = c.drowning;
-               if(drowning_damage > 0 && lplayer->hp > 0){
-                       u16 breath = lplayer->getBreath();
-                       if(breath > 10){
-                               breath = 11;
-                       }
-                       if(breath > 0){
-                               breath -= 1;
+       // Protocol v29 make this behaviour obsolete
+       if (((Client*) getGameDef())->getProtoVersion() < 29) {
+               /*
+                       Drowning
+               */
+               if (m_drowning_interval.step(dtime, 2.0)) {
+                       v3f pf = lplayer->getPosition();
+
+                       // head
+                       v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
+                       MapNode n = m_map->getNodeNoEx(p);
+                       ContentFeatures c = m_gamedef->ndef()->get(n);
+                       u8 drowning_damage = c.drowning;
+                       if (drowning_damage > 0 && lplayer->hp > 0) {
+                               u16 breath = lplayer->getBreath();
+                               if (breath > 10) {
+                                       breath = 11;
+                               }
+                               if (breath > 0) {
+                                       breath -= 1;
+                               }
+                               lplayer->setBreath(breath);
+                               updateLocalPlayerBreath(breath);
                        }
-                       lplayer->setBreath(breath);
-                       updateLocalPlayerBreath(breath);
-               }
 
-               if(lplayer->getBreath() == 0 && drowning_damage > 0){
-                       damageLocalPlayer(drowning_damage, true);
+                       if (lplayer->getBreath() == 0 && drowning_damage > 0) {
+                               damageLocalPlayer(drowning_damage, true);
+                       }
                }
-       }
-       if(m_breathing_interval.step(dtime, 0.5))
-       {
-               v3f pf = lplayer->getPosition();
-
-               // head
-               v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
-               MapNode n = m_map->getNodeNoEx(p);
-               ContentFeatures c = m_gamedef->ndef()->get(n);
-               if (!lplayer->hp){
-                       lplayer->setBreath(11);
-               }
-               else if(c.drowning == 0){
-                       u16 breath = lplayer->getBreath();
-                       if(breath <= 10){
-                               breath += 1;
-                               lplayer->setBreath(breath);
-                               updateLocalPlayerBreath(breath);
+               if (m_breathing_interval.step(dtime, 0.5)) {
+                       v3f pf = lplayer->getPosition();
+
+                       // head
+                       v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
+                       MapNode n = m_map->getNodeNoEx(p);
+                       ContentFeatures c = m_gamedef->ndef()->get(n);
+                       if (!lplayer->hp) {
+                               lplayer->setBreath(11);
+                       } else if (c.drowning == 0) {
+                               u16 breath = lplayer->getBreath();
+                               if (breath <= 10) {
+                                       breath += 1;
+                                       lplayer->setBreath(breath);
+                                       updateLocalPlayerBreath(breath);
+                               }
                        }
                }
        }
index 3364de8c5340571ac727093f377db24caba2103e..6defdcf1be746bcdfb01373fedc5fa2c7f2771c3 100644 (file)
@@ -193,7 +193,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
        null_command_factory, // 0x3f
        { "TOSERVER_REQUEST_MEDIA",      1, true }, // 0x40
        { "TOSERVER_RECEIVED_MEDIA",     1, true }, // 0x41
-       { "TOSERVER_BREATH",             0, true }, // 0x42
+       null_command_factory, // 0x42 old TOSERVER_BREATH. Ignored by servers
        { "TOSERVER_CLIENT_READY",       0, true }, // 0x43
        null_command_factory, // 0x44
        null_command_factory, // 0x45
index 018b392b63b9134ff0ff00f39d0d774f931f6d64..f65167380c815c47180991877b487e69fa2c7519 100644 (file)
@@ -138,9 +138,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Add nodedef v3 - connected nodeboxes
        PROTOCOL_VERSION 28:
                CPT2_MESHOPTIONS
+       PROTOCOL_VERSION 29:
+               Server doesn't accept TOSERVER_BREATH anymore
 */
 
-#define LATEST_PROTOCOL_VERSION 28
+#define LATEST_PROTOCOL_VERSION 29
 
 // Server's supported network protocol range
 #define SERVER_PROTOCOL_VERSION_MIN 13
@@ -833,7 +835,7 @@ enum ToServerCommand
                <no payload data>
        */
 
-       TOSERVER_BREATH = 0x42,
+       TOSERVER_BREATH = 0x42, // Obsolete
        /*
                u16 breath
        */
index 9b14a1be3aa00d43e32bbd32e743287e6d0a471e..642dd376a62e7ff77cb60724483fba612fae2cd2 100644 (file)
@@ -90,7 +90,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
        null_command_handler, // 0x3f
        { "TOSERVER_REQUEST_MEDIA",            TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
        { "TOSERVER_RECEIVED_MEDIA",           TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
-       { "TOSERVER_BREATH",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42
+       { "TOSERVER_BREATH",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x42 Old breath model which is now deprecated for anticheating
        { "TOSERVER_CLIENT_READY",             TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
        null_command_handler, // 0x44
        null_command_handler, // 0x45
index d0f4d948d8b6ff7e3bd6f36c84f66756cc23c51f..eeabcca71947f3d56627c25138afda0fdc01482d 100644 (file)
@@ -1136,46 +1136,6 @@ void Server::handleCommand_Damage(NetworkPacket* pkt)
        }
 }
 
-void Server::handleCommand_Breath(NetworkPacket* pkt)
-{
-       u16 breath;
-
-       *pkt >> breath;
-
-       RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
-
-       if (player == NULL) {
-               errorstream << "Server::ProcessData(): Canceling: "
-                               "No player for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-
-       PlayerSAO *playersao = player->getPlayerSAO();
-       if (playersao == NULL) {
-               errorstream << "Server::ProcessData(): Canceling: "
-                               "No player object for peer_id=" << pkt->getPeerId()
-                               << " disconnecting peer!" << std::endl;
-               m_con.DisconnectPeer(pkt->getPeerId());
-               return;
-       }
-
-       /*
-        * If player is dead, we don't need to update the breath
-        * He is dead !
-        */
-       if (playersao->isDead()) {
-               verbosestream << "TOSERVER_BREATH: " << player->getName()
-                               << " is dead. Ignoring packet";
-               return;
-       }
-
-       playersao->setBreath(breath);
-       SendPlayerBreath(pkt->getPeerId());
-}
-
 void Server::handleCommand_Password(NetworkPacket* pkt)
 {
        if (pkt->getSize() != PASSWORD_SIZE * 2)
index 67ab89113aa73a88fd83aac701cedea10fe97798..18bfa1030894fd2dd6e98a83b69f1281d6a23e7c 100644 (file)
@@ -148,7 +148,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
                } catch (SettingNotFoundException &e) {}
 
                try {
-                       sao->setBreath(args.getS32("breath"));
+                       sao->setBreath(args.getS32("breath"), false);
                } catch (SettingNotFoundException &e) {}
        }
 
index 2a8b8a64eec67e2719f111cdb2bec606319fb76f..cfdceb28e7816472746888db34c0d4423cd78ac5 100644 (file)
@@ -1152,13 +1152,8 @@ int ObjectRef::l_set_breath(lua_State *L)
        PlayerSAO* co = getplayersao(ref);
        if (co == NULL) return 0;
        u16 breath = luaL_checknumber(L, 2);
-       // Do it
        co->setBreath(breath);
 
-       // If the object is a player sent the breath to client
-       if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
-                       getServer(L)->SendPlayerBreath(((PlayerSAO*)co)->getPeerID());
-
        return 0;
 }
 
index fa7a838d428cfa673932ca962ab539c268baa116..60dbef0d266f94b9fde23b9b13e84c611addf4e2 100644 (file)
@@ -1076,8 +1076,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        }
        m_clients.unlock();
 
-       RemotePlayer *player =
-               static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
+       RemotePlayer *player = m_env->getPlayer(playername.c_str());
 
        // If failed, cancel
        if ((playersao == NULL) || (player == NULL)) {
@@ -1113,7 +1112,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        SendPlayerHPOrDie(playersao);
 
        // Send Breath
-       SendPlayerBreath(peer_id);
+       SendPlayerBreath(playersao);
 
        // Show death screen if necessary
        if (playersao->isDead())
@@ -1857,14 +1856,13 @@ void Server::SendPlayerHP(u16 peer_id)
        playersao->m_messages_out.push(aom);
 }
 
-void Server::SendPlayerBreath(u16 peer_id)
+void Server::SendPlayerBreath(PlayerSAO *sao)
 {
        DSTACK(FUNCTION_NAME);
-       PlayerSAO *playersao = getPlayerSAO(peer_id);
-       assert(playersao);
+       assert(sao);
 
-       m_script->player_event(playersao, "breath_changed");
-       SendBreath(peer_id, playersao->getBreath());
+       m_script->player_event(sao, "breath_changed");
+       SendBreath(sao->getPeerID(), sao->getBreath());
 }
 
 void Server::SendMovePlayer(u16 peer_id)
@@ -2565,7 +2563,6 @@ void Server::RespawnPlayer(u16 peer_id)
        }
 
        SendPlayerHP(peer_id);
-       SendPlayerBreath(peer_id);
 }
 
 
index cab7e24458219127449a90b210aae48fb78d48c8..f0df0f9ec6d8fdd7639d25918f04ec6a86f247e7 100644 (file)
@@ -180,7 +180,6 @@ public:
        void handleCommand_InventoryAction(NetworkPacket* pkt);
        void handleCommand_ChatMessage(NetworkPacket* pkt);
        void handleCommand_Damage(NetworkPacket* pkt);
-       void handleCommand_Breath(NetworkPacket* pkt);
        void handleCommand_Password(NetworkPacket* pkt);
        void handleCommand_PlayerItem(NetworkPacket* pkt);
        void handleCommand_Respawn(NetworkPacket* pkt);
@@ -358,7 +357,7 @@ public:
        void printToConsoleOnly(const std::string &text);
 
        void SendPlayerHPOrDie(PlayerSAO *player);
-       void SendPlayerBreath(u16 peer_id);
+       void SendPlayerBreath(PlayerSAO *sao);
        void SendInventory(PlayerSAO* playerSAO);
        void SendMovePlayer(u16 peer_id);
 
index 85fbc8b2d80f0ab053cc22c03498f21b8626cbfc..655ee08fd7c87ffa98ec2f294f9833dfacfa46f9 100644 (file)
@@ -49,7 +49,7 @@ void TestPlayer::testSave(IGameDef *gamedef)
        PlayerSAO sao(NULL, 1, false);
        sao.initialize(&rplayer, std::set<std::string>());
        rplayer.setPlayerSAO(&sao);
-       sao.setBreath(10);
+       sao.setBreath(10, false);
        sao.setHPRaw(8);
        sao.setYaw(0.1f);
        sao.setPitch(0.6f);
@@ -64,7 +64,7 @@ void TestPlayer::testLoad(IGameDef *gamedef)
        PlayerSAO sao(NULL, 1, false);
        sao.initialize(&rplayer, std::set<std::string>());
        rplayer.setPlayerSAO(&sao);
-       sao.setBreath(10);
+       sao.setBreath(10, false);
        sao.setHPRaw(8);
        sao.setYaw(0.1f);
        sao.setPitch(0.6f);