Breath cheat fix: server side

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
master
Loic Blot 2017-01-01 16:13:01 +01:00
parent a1346c916e
commit 52ba1f867e
13 changed files with 107 additions and 113 deletions

View File

@ -499,7 +499,8 @@ void Client::step(float dtime)
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }
} }
else if(event.type == CEE_PLAYER_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; u16 breath = event.player_breath.amount;
sendBreath(breath); sendBreath(breath);
} }
@ -1270,6 +1271,10 @@ void Client::sendBreath(u16 breath)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
// Protocol v29 make this obsolete
if (m_proto_ver >= 29)
return;
NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16)); NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
pkt << breath; pkt << breath;
Send(&pkt); Send(&pkt);

View File

@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h" // For compressZlib #include "serialization.h" // For compressZlib
#include "tool.h" // For ToolCapabilities #include "tool.h" // For ToolCapabilities
#include "gamedef.h" #include "gamedef.h"
#include "nodedef.h"
#include "remoteplayer.h" #include "remoteplayer.h"
#include "server.h" #include "server.h"
#include "scripting_game.h" #include "scripting_game.h"
@ -940,8 +941,35 @@ bool PlayerSAO::isAttached()
void PlayerSAO::step(float dtime, bool send_recommended) 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; m_properties_sent = true;
std::string str = getPropertyPacket(); std::string str = getPropertyPacket();
// create message and add to list // create message and add to list
@ -1237,12 +1265,15 @@ void PlayerSAO::setHP(s16 hp)
m_properties_sent = false; m_properties_sent = false;
} }
void PlayerSAO::setBreath(const u16 breath) void PlayerSAO::setBreath(const u16 breath, bool send)
{ {
if (m_player && breath != m_breath) if (m_player && breath != m_breath)
m_player->setDirty(true); 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) void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)

View File

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef CONTENT_SAO_HEADER #ifndef CONTENT_SAO_HEADER
#define CONTENT_SAO_HEADER #define CONTENT_SAO_HEADER
#include <util/numeric.h>
#include "serverobject.h" #include "serverobject.h"
#include "itemgroup.h" #include "itemgroup.h"
#include "object_properties.h" #include "object_properties.h"
@ -232,7 +233,7 @@ public:
void setHPRaw(s16 hp) { m_hp = hp; } void setHPRaw(s16 hp) { m_hp = hp; }
s16 readDamage(); s16 readDamage();
u16 getBreath() const { return m_breath; } 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); void setArmorGroups(const ItemGroupList &armor_groups);
ItemGroupList getArmorGroups(); ItemGroupList getArmorGroups();
void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop);
@ -339,6 +340,10 @@ private:
v3s16 m_nocheat_dig_pos; v3s16 m_nocheat_dig_pos;
float m_nocheat_dig_time; float m_nocheat_dig_time;
// Timers
IntervalLimiter m_breathing_interval;
IntervalLimiter m_drowning_interval;
int m_wield_index; int m_wield_index;
bool m_position_not_sent; bool m_position_not_sent;
ItemGroupList m_armor_groups; ItemGroupList m_armor_groups;

View File

@ -2511,11 +2511,12 @@ void ClientEnvironment::step(float dtime)
} }
} }
// Protocol v29 make this behaviour obsolete
if (((Client*) getGameDef())->getProtoVersion() < 29) {
/* /*
Drowning Drowning
*/ */
if(m_drowning_interval.step(dtime, 2.0)) if (m_drowning_interval.step(dtime, 2.0)) {
{
v3f pf = lplayer->getPosition(); v3f pf = lplayer->getPosition();
// head // head
@ -2539,8 +2540,7 @@ void ClientEnvironment::step(float dtime)
damageLocalPlayer(drowning_damage, true); damageLocalPlayer(drowning_damage, true);
} }
} }
if(m_breathing_interval.step(dtime, 0.5)) if (m_breathing_interval.step(dtime, 0.5)) {
{
v3f pf = lplayer->getPosition(); v3f pf = lplayer->getPosition();
// head // head
@ -2549,8 +2549,7 @@ void ClientEnvironment::step(float dtime)
ContentFeatures c = m_gamedef->ndef()->get(n); ContentFeatures c = m_gamedef->ndef()->get(n);
if (!lplayer->hp) { if (!lplayer->hp) {
lplayer->setBreath(11); lplayer->setBreath(11);
} } else if (c.drowning == 0) {
else if(c.drowning == 0){
u16 breath = lplayer->getBreath(); u16 breath = lplayer->getBreath();
if (breath <= 10) { if (breath <= 10) {
breath += 1; breath += 1;
@ -2559,6 +2558,7 @@ void ClientEnvironment::step(float dtime)
} }
} }
} }
}
// Update lighting on local player (used for wield item) // Update lighting on local player (used for wield item)
u32 day_night_ratio = getDayNightRatio(); u32 day_night_ratio = getDayNightRatio();

View File

@ -193,7 +193,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
null_command_factory, // 0x3f null_command_factory, // 0x3f
{ "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
{ "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 { "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 { "TOSERVER_CLIENT_READY", 0, true }, // 0x43
null_command_factory, // 0x44 null_command_factory, // 0x44
null_command_factory, // 0x45 null_command_factory, // 0x45

View File

@ -138,9 +138,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Add nodedef v3 - connected nodeboxes Add nodedef v3 - connected nodeboxes
PROTOCOL_VERSION 28: PROTOCOL_VERSION 28:
CPT2_MESHOPTIONS 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 // Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13 #define SERVER_PROTOCOL_VERSION_MIN 13
@ -833,7 +835,7 @@ enum ToServerCommand
<no payload data> <no payload data>
*/ */
TOSERVER_BREATH = 0x42, TOSERVER_BREATH = 0x42, // Obsolete
/* /*
u16 breath u16 breath
*/ */

View File

@ -90,7 +90,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
null_command_handler, // 0x3f null_command_handler, // 0x3f
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
{ "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41 { "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 { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
null_command_handler, // 0x44 null_command_handler, // 0x44
null_command_handler, // 0x45 null_command_handler, // 0x45

View 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) void Server::handleCommand_Password(NetworkPacket* pkt)
{ {
if (pkt->getSize() != PASSWORD_SIZE * 2) if (pkt->getSize() != PASSWORD_SIZE * 2)

View File

@ -148,7 +148,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
} catch (SettingNotFoundException &e) {} } catch (SettingNotFoundException &e) {}
try { try {
sao->setBreath(args.getS32("breath")); sao->setBreath(args.getS32("breath"), false);
} catch (SettingNotFoundException &e) {} } catch (SettingNotFoundException &e) {}
} }

View File

@ -1152,13 +1152,8 @@ int ObjectRef::l_set_breath(lua_State *L)
PlayerSAO* co = getplayersao(ref); PlayerSAO* co = getplayersao(ref);
if (co == NULL) return 0; if (co == NULL) return 0;
u16 breath = luaL_checknumber(L, 2); u16 breath = luaL_checknumber(L, 2);
// Do it
co->setBreath(breath); 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; return 0;
} }

View File

@ -1076,8 +1076,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
} }
m_clients.unlock(); m_clients.unlock();
RemotePlayer *player = RemotePlayer *player = m_env->getPlayer(playername.c_str());
static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
// If failed, cancel // If failed, cancel
if ((playersao == NULL) || (player == NULL)) { if ((playersao == NULL) || (player == NULL)) {
@ -1113,7 +1112,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
SendPlayerHPOrDie(playersao); SendPlayerHPOrDie(playersao);
// Send Breath // Send Breath
SendPlayerBreath(peer_id); SendPlayerBreath(playersao);
// Show death screen if necessary // Show death screen if necessary
if (playersao->isDead()) if (playersao->isDead())
@ -1857,14 +1856,13 @@ void Server::SendPlayerHP(u16 peer_id)
playersao->m_messages_out.push(aom); playersao->m_messages_out.push(aom);
} }
void Server::SendPlayerBreath(u16 peer_id) void Server::SendPlayerBreath(PlayerSAO *sao)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id); assert(sao);
assert(playersao);
m_script->player_event(playersao, "breath_changed"); m_script->player_event(sao, "breath_changed");
SendBreath(peer_id, playersao->getBreath()); SendBreath(sao->getPeerID(), sao->getBreath());
} }
void Server::SendMovePlayer(u16 peer_id) void Server::SendMovePlayer(u16 peer_id)
@ -2565,7 +2563,6 @@ void Server::RespawnPlayer(u16 peer_id)
} }
SendPlayerHP(peer_id); SendPlayerHP(peer_id);
SendPlayerBreath(peer_id);
} }

View File

@ -180,7 +180,6 @@ public:
void handleCommand_InventoryAction(NetworkPacket* pkt); void handleCommand_InventoryAction(NetworkPacket* pkt);
void handleCommand_ChatMessage(NetworkPacket* pkt); void handleCommand_ChatMessage(NetworkPacket* pkt);
void handleCommand_Damage(NetworkPacket* pkt); void handleCommand_Damage(NetworkPacket* pkt);
void handleCommand_Breath(NetworkPacket* pkt);
void handleCommand_Password(NetworkPacket* pkt); void handleCommand_Password(NetworkPacket* pkt);
void handleCommand_PlayerItem(NetworkPacket* pkt); void handleCommand_PlayerItem(NetworkPacket* pkt);
void handleCommand_Respawn(NetworkPacket* pkt); void handleCommand_Respawn(NetworkPacket* pkt);
@ -358,7 +357,7 @@ public:
void printToConsoleOnly(const std::string &text); void printToConsoleOnly(const std::string &text);
void SendPlayerHPOrDie(PlayerSAO *player); void SendPlayerHPOrDie(PlayerSAO *player);
void SendPlayerBreath(u16 peer_id); void SendPlayerBreath(PlayerSAO *sao);
void SendInventory(PlayerSAO* playerSAO); void SendInventory(PlayerSAO* playerSAO);
void SendMovePlayer(u16 peer_id); void SendMovePlayer(u16 peer_id);

View File

@ -49,7 +49,7 @@ void TestPlayer::testSave(IGameDef *gamedef)
PlayerSAO sao(NULL, 1, false); PlayerSAO sao(NULL, 1, false);
sao.initialize(&rplayer, std::set<std::string>()); sao.initialize(&rplayer, std::set<std::string>());
rplayer.setPlayerSAO(&sao); rplayer.setPlayerSAO(&sao);
sao.setBreath(10); sao.setBreath(10, false);
sao.setHPRaw(8); sao.setHPRaw(8);
sao.setYaw(0.1f); sao.setYaw(0.1f);
sao.setPitch(0.6f); sao.setPitch(0.6f);
@ -64,7 +64,7 @@ void TestPlayer::testLoad(IGameDef *gamedef)
PlayerSAO sao(NULL, 1, false); PlayerSAO sao(NULL, 1, false);
sao.initialize(&rplayer, std::set<std::string>()); sao.initialize(&rplayer, std::set<std::string>());
rplayer.setPlayerSAO(&sao); rplayer.setPlayerSAO(&sao);
sao.setBreath(10); sao.setBreath(10, false);
sao.setHPRaw(8); sao.setHPRaw(8);
sao.setYaw(0.1f); sao.setYaw(0.1f);
sao.setPitch(0.6f); sao.setPitch(0.6f);