Move PlayerSAO to dedicated files
authorLoic Blot <loic.blot@unix-experience.fr>
Sat, 11 Apr 2020 07:59:09 +0000 (09:59 +0200)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Sat, 11 Apr 2020 14:07:17 +0000 (16:07 +0200)
19 files changed:
src/clientiface.cpp
src/content_sao.cpp
src/content_sao.h
src/database/database-files.cpp
src/database/database-postgresql.cpp
src/database/database-sqlite3.cpp
src/network/serverpackethandler.cpp
src/remoteplayer.cpp
src/script/common/c_content.cpp
src/script/cpp_api/s_base.cpp
src/script/cpp_api/s_player.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_object.cpp
src/server.cpp
src/server/CMakeLists.txt
src/server/player_sao.cpp [new file with mode: 0644]
src/server/player_sao.h [new file with mode: 0644]
src/server/unit_sao.cpp
src/serverenvironment.cpp

index dceaa64f25457dfead57de719a9778c88e185f1a..17237f73ee545778e92218108fcca82419f70cb4 100644 (file)
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <sstream>
 #include "clientiface.h"
+#include "content_sao.h"
 #include "network/connection.h"
 #include "network/serveropcodes.h"
 #include "remoteplayer.h"
@@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serverenvironment.h"
 #include "map.h"
 #include "emerge.h"
-#include "content_sao.h"              // TODO this is used for cleanup of only
+#include "server/player_sao.h"
 #include "log.h"
 #include "util/srp.h"
 #include "face_position_cache.h"
index 0d387b53a59fdd94b89a029b66746062d1386613..7ec17aa82488c056f4b43f92db998b74faaa1dd2 100644 (file)
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "remoteplayer.h"
 #include "server.h"
 #include "scripting_server.h"
+#include "server/player_sao.h"
 #include "settings.h"
 #include <algorithm>
 #include <cmath>
@@ -678,678 +679,3 @@ bool LuaEntitySAO::collideWithObjects() const
 {
        return m_prop.collideWithObjects;
 }
-
-/*
-       PlayerSAO
-*/
-
-// No prototype, PlayerSAO does not need to be deserialized
-
-PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
-               bool is_singleplayer):
-       UnitSAO(env_, v3f(0,0,0)),
-       m_player(player_),
-       m_peer_id(peer_id_),
-       m_is_singleplayer(is_singleplayer)
-{
-       SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT);
-
-       m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
-       m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
-       m_prop.physical = false;
-       m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
-       m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
-       m_prop.pointable = true;
-       // Start of default appearance, this should be overwritten by Lua
-       m_prop.visual = "upright_sprite";
-       m_prop.visual_size = v3f(1, 2, 1);
-       m_prop.textures.clear();
-       m_prop.textures.emplace_back("player.png");
-       m_prop.textures.emplace_back("player_back.png");
-       m_prop.colors.clear();
-       m_prop.colors.emplace_back(255, 255, 255, 255);
-       m_prop.spritediv = v2s16(1,1);
-       m_prop.eye_height = 1.625f;
-       // End of default appearance
-       m_prop.is_visible = true;
-       m_prop.backface_culling = false;
-       m_prop.makes_footstep_sound = true;
-       m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
-       m_hp = m_prop.hp_max;
-       m_breath = m_prop.breath_max;
-       // Disable zoom in survival mode using a value of 0
-       m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
-
-       if (!g_settings->getBool("enable_damage"))
-               m_armor_groups["immortal"] = 1;
-}
-
-void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
-{
-       assert(player);
-       m_player = player;
-       m_privs = privs;
-}
-
-v3f PlayerSAO::getEyeOffset() const
-{
-       return v3f(0, BS * m_prop.eye_height, 0);
-}
-
-std::string PlayerSAO::getDescription()
-{
-       return std::string("player ") + m_player->getName();
-}
-
-// Called after id has been set and has been inserted in environment
-void PlayerSAO::addedToEnvironment(u32 dtime_s)
-{
-       ServerActiveObject::addedToEnvironment(dtime_s);
-       ServerActiveObject::setBasePosition(m_base_position);
-       m_player->setPlayerSAO(this);
-       m_player->setPeerId(m_peer_id);
-       m_last_good_position = m_base_position;
-}
-
-// Called before removing from environment
-void PlayerSAO::removingFromEnvironment()
-{
-       ServerActiveObject::removingFromEnvironment();
-       if (m_player->getPlayerSAO() == this) {
-               unlinkPlayerSessionAndSave();
-               for (u32 attached_particle_spawner : m_attached_particle_spawners) {
-                       m_env->deleteParticleSpawner(attached_particle_spawner, false);
-               }
-       }
-}
-
-std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
-{
-       std::ostringstream os(std::ios::binary);
-
-       // Protocol >= 15
-       writeU8(os, 1); // version
-       os << serializeString(m_player->getName()); // name
-       writeU8(os, 1); // is_player
-       writeS16(os, getId()); // id
-       writeV3F32(os, m_base_position);
-       writeV3F32(os, m_rotation);
-       writeU16(os, getHP());
-
-       std::ostringstream msg_os(std::ios::binary);
-       msg_os << serializeLongString(getPropertyPacket()); // message 1
-       msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2
-       msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3
-       for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
-                       ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
-               msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first,
-                       (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
-       }
-       msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4
-       msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5
-       // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
-       msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6
-       int message_count = 6 + m_bone_position.size();
-       for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
-                       ii != m_attachment_child_ids.end(); ++ii) {
-               if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
-                       message_count++;
-                       msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version));
-               }
-       }
-
-       writeU8(os, message_count);
-       os.write(msg_os.str().c_str(), msg_os.str().size());
-
-       // return result
-       return os.str();
-}
-
-void PlayerSAO::getStaticData(std::string * result) const
-{
-       FATAL_ERROR("Obsolete function");
-}
-
-void PlayerSAO::step(float dtime, bool send_recommended)
-{
-       if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) {
-               // Get nose/mouth position, approximate with eye position
-               v3s16 p = floatToInt(getEyePosition(), BS);
-               MapNode n = m_env->getMap().getNode(p);
-               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
-               // If node generates drown
-               if (c.drowning > 0 && m_hp > 0) {
-                       if (m_breath > 0)
-                               setBreath(m_breath - 1);
-
-                       // No more breath, damage player
-                       if (m_breath == 0) {
-                               PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
-                               setHP(m_hp - c.drowning, reason);
-                               m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
-                       }
-               }
-       }
-
-       if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) {
-               // Get nose/mouth position, approximate with eye position
-               v3s16 p = floatToInt(getEyePosition(), BS);
-               MapNode n = m_env->getMap().getNode(p);
-               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
-               // If player is alive & not drowning & not in ignore & not immortal, breathe
-               if (m_breath < m_prop.breath_max && c.drowning == 0 &&
-                               n.getContent() != CONTENT_IGNORE && m_hp > 0)
-                       setBreath(m_breath + 1);
-       }
-
-       if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) {
-               u32 damage_per_second = 0;
-               std::string nodename;
-               // Lowest and highest damage points are 0.1 within collisionbox
-               float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
-
-               // Sequence of damage points, starting 0.1 above feet and progressing
-               // upwards in 1 node intervals, stopping below top damage point.
-               for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
-                       v3s16 p = floatToInt(m_base_position +
-                               v3f(0.0f, dam_height * BS, 0.0f), BS);
-                       MapNode n = m_env->getMap().getNode(p);
-                       const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
-                       if (c.damage_per_second > damage_per_second) {
-                               damage_per_second = c.damage_per_second;
-                               nodename = c.name;
-                       }
-               }
-
-               // Top damage point
-               v3s16 ptop = floatToInt(m_base_position +
-                       v3f(0.0f, dam_top * BS, 0.0f), BS);
-               MapNode ntop = m_env->getMap().getNode(ptop);
-               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop);
-               if (c.damage_per_second > damage_per_second) {
-                       damage_per_second = c.damage_per_second;
-                       nodename = c.name;
-               }
-
-               if (damage_per_second != 0 && m_hp > 0) {
-                       s32 newhp = (s32)m_hp - (s32)damage_per_second;
-                       PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename);
-                       setHP(newhp, reason);
-                       m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
-               }
-       }
-
-       if (!m_properties_sent) {
-               m_properties_sent = true;
-               std::string str = getPropertyPacket();
-               // create message and add to list
-               ActiveObjectMessage aom(getId(), true, str);
-               m_messages_out.push(aom);
-               m_env->getScriptIface()->player_event(this, "properties_changed");
-       }
-
-       // If attached, check that our parent is still there. If it isn't, detach.
-       if (m_attachment_parent_id && !isAttached()) {
-               m_attachment_parent_id = 0;
-               m_attachment_bone = "";
-               m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
-               m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
-               setBasePosition(m_last_good_position);
-               m_env->getGameDef()->SendMovePlayer(m_peer_id);
-       }
-
-       //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
-
-       // Set lag pool maximums based on estimated lag
-       const float LAG_POOL_MIN = 5.0f;
-       float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
-       if(lag_pool_max < LAG_POOL_MIN)
-               lag_pool_max = LAG_POOL_MIN;
-       m_dig_pool.setMax(lag_pool_max);
-       m_move_pool.setMax(lag_pool_max);
-
-       // Increment cheat prevention timers
-       m_dig_pool.add(dtime);
-       m_move_pool.add(dtime);
-       m_time_from_last_teleport += dtime;
-       m_time_from_last_punch += dtime;
-       m_nocheat_dig_time += dtime;
-       m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f);
-
-       // Each frame, parent position is copied if the object is attached,
-       // otherwise it's calculated normally.
-       // If the object gets detached this comes into effect automatically from
-       // the last known origin.
-       if (isAttached()) {
-               v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
-               m_last_good_position = pos;
-               setBasePosition(pos);
-       }
-
-       if (!send_recommended)
-               return;
-
-       if (m_position_not_sent) {
-               m_position_not_sent = false;
-               float update_interval = m_env->getSendRecommendedInterval();
-               v3f pos;
-               // When attached, the position is only sent to clients where the
-               // parent isn't known
-               if (isAttached())
-                       pos = m_last_good_position;
-               else
-                       pos = m_base_position;
-
-               std::string str = generateUpdatePositionCommand(
-                       pos,
-                       v3f(0.0f, 0.0f, 0.0f),
-                       v3f(0.0f, 0.0f, 0.0f),
-                       m_rotation,
-                       true,
-                       false,
-                       update_interval
-               );
-               // create message and add to list
-               m_messages_out.emplace(getId(), false, str);
-       }
-
-       if (!m_armor_groups_sent) {
-               m_armor_groups_sent = true;
-               // create message and add to list
-               m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand());
-       }
-
-       if (!m_physics_override_sent) {
-               m_physics_override_sent = true;
-               // create message and add to list
-               m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand());
-       }
-
-       if (!m_animation_sent) {
-               m_animation_sent = true;
-               // create message and add to list
-               m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand());
-       }
-
-       if (!m_bone_position_sent) {
-               m_bone_position_sent = true;
-               for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
-                               ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
-                       std::string str = generateUpdateBonePositionCommand((*ii).first,
-                                       (*ii).second.X, (*ii).second.Y);
-                       // create message and add to list
-                       m_messages_out.emplace(getId(), true, str);
-               }
-       }
-
-       if (!m_attachment_sent) {
-               m_attachment_sent = true;
-               std::string str = generateUpdateAttachmentCommand();
-               // create message and add to list
-               ActiveObjectMessage aom(getId(), true, str);
-               m_messages_out.push(aom);
-       }
-}
-
-std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const
-{
-       std::ostringstream os(std::ios::binary);
-       // command
-       writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE);
-       // parameters
-       writeF32(os, m_physics_override_speed);
-       writeF32(os, m_physics_override_jump);
-       writeF32(os, m_physics_override_gravity);
-       // these are sent inverted so we get true when the server sends nothing
-       writeU8(os, !m_physics_override_sneak);
-       writeU8(os, !m_physics_override_sneak_glitch);
-       writeU8(os, !m_physics_override_new_move);
-       return os.str();
-}
-
-void PlayerSAO::setBasePosition(const v3f &position)
-{
-       if (m_player && position != m_base_position)
-               m_player->setDirty(true);
-
-       // This needs to be ran for attachments too
-       ServerActiveObject::setBasePosition(position);
-
-       // Updating is not wanted/required for player migration
-       if (m_env) {
-               m_position_not_sent = true;
-       }
-}
-
-void PlayerSAO::setPos(const v3f &pos)
-{
-       if(isAttached())
-               return;
-
-       // Send mapblock of target location
-       v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE);
-       m_env->getGameDef()->SendBlock(m_peer_id, blockpos);
-
-       setBasePosition(pos);
-       // Movement caused by this command is always valid
-       m_last_good_position = pos;
-       m_move_pool.empty();
-       m_time_from_last_teleport = 0.0;
-       m_env->getGameDef()->SendMovePlayer(m_peer_id);
-}
-
-void PlayerSAO::moveTo(v3f pos, bool continuous)
-{
-       if(isAttached())
-               return;
-
-       setBasePosition(pos);
-       // Movement caused by this command is always valid
-       m_last_good_position = pos;
-       m_move_pool.empty();
-       m_time_from_last_teleport = 0.0;
-       m_env->getGameDef()->SendMovePlayer(m_peer_id);
-}
-
-void PlayerSAO::setPlayerYaw(const float yaw)
-{
-       v3f rotation(0, yaw, 0);
-       if (m_player && yaw != m_rotation.Y)
-               m_player->setDirty(true);
-
-       // Set player model yaw, not look view
-       UnitSAO::setRotation(rotation);
-}
-
-void PlayerSAO::setFov(const float fov)
-{
-       if (m_player && fov != m_fov)
-               m_player->setDirty(true);
-
-       m_fov = fov;
-}
-
-void PlayerSAO::setWantedRange(const s16 range)
-{
-       if (m_player && range != m_wanted_range)
-               m_player->setDirty(true);
-
-       m_wanted_range = range;
-}
-
-void PlayerSAO::setPlayerYawAndSend(const float yaw)
-{
-       setPlayerYaw(yaw);
-       m_env->getGameDef()->SendMovePlayer(m_peer_id);
-}
-
-void PlayerSAO::setLookPitch(const float pitch)
-{
-       if (m_player && pitch != m_pitch)
-               m_player->setDirty(true);
-
-       m_pitch = pitch;
-}
-
-void PlayerSAO::setLookPitchAndSend(const float pitch)
-{
-       setLookPitch(pitch);
-       m_env->getGameDef()->SendMovePlayer(m_peer_id);
-}
-
-u16 PlayerSAO::punch(v3f dir,
-       const ToolCapabilities *toolcap,
-       ServerActiveObject *puncher,
-       float time_from_last_punch)
-{
-       if (!toolcap)
-               return 0;
-
-       FATAL_ERROR_IF(!puncher, "Punch action called without SAO");
-
-       // No effect if PvP disabled or if immortal
-       if (isImmortal() || !g_settings->getBool("enable_pvp")) {
-               if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
-                       // create message and add to list
-                       sendPunchCommand();
-                       return 0;
-               }
-       }
-
-       s32 old_hp = getHP();
-       HitParams hitparams = getHitParams(m_armor_groups, toolcap,
-                       time_from_last_punch);
-
-       PlayerSAO *playersao = m_player->getPlayerSAO();
-
-       bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
-                               puncher, time_from_last_punch, toolcap, dir,
-                               hitparams.hp);
-
-       if (!damage_handled) {
-               setHP((s32)getHP() - (s32)hitparams.hp,
-                               PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
-       } else { // override client prediction
-               if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
-                       // create message and add to list
-                       sendPunchCommand();
-               }
-       }
-
-       actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
-               ", hp=" << puncher->getHP() << ") punched " <<
-               getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
-               "), damage=" << (old_hp - (s32)getHP()) <<
-               (damage_handled ? " (handled by Lua)" : "") << std::endl;
-
-       return hitparams.wear;
-}
-
-void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
-{
-       s32 oldhp = m_hp;
-
-       hp = rangelim(hp, 0, m_prop.hp_max);
-
-       if (oldhp != hp) {
-               s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
-               if (hp_change == 0)
-                       return;
-
-               hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max);
-       }
-
-       if (hp < oldhp && isImmortal())
-               return;
-
-       m_hp = hp;
-
-       // Update properties on death
-       if ((hp == 0) != (oldhp == 0))
-               m_properties_sent = false;
-}
-
-void PlayerSAO::setBreath(const u16 breath, bool send)
-{
-       if (m_player && breath != m_breath)
-               m_player->setDirty(true);
-
-       m_breath = rangelim(breath, 0, m_prop.breath_max);
-
-       if (send)
-               m_env->getGameDef()->SendPlayerBreath(this);
-}
-
-Inventory *PlayerSAO::getInventory() const
-{
-       return m_player ? &m_player->inventory : nullptr;
-}
-
-InventoryLocation PlayerSAO::getInventoryLocation() const
-{
-       InventoryLocation loc;
-       loc.setPlayer(m_player->getName());
-       return loc;
-}
-
-u16 PlayerSAO::getWieldIndex() const
-{
-       return m_player->getWieldIndex();
-}
-
-ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const
-{
-       return m_player->getWieldedItem(selected, hand);
-}
-
-bool PlayerSAO::setWieldedItem(const ItemStack &item)
-{
-       InventoryList *mlist = m_player->inventory.getList(getWieldList());
-       if (mlist) {
-               mlist->changeItem(m_player->getWieldIndex(), item);
-               return true;
-       }
-       return false;
-}
-
-void PlayerSAO::disconnected()
-{
-       m_peer_id = PEER_ID_INEXISTENT;
-       m_pending_removal = true;
-}
-
-void PlayerSAO::unlinkPlayerSessionAndSave()
-{
-       assert(m_player->getPlayerSAO() == this);
-       m_player->setPeerId(PEER_ID_INEXISTENT);
-       m_env->savePlayer(m_player);
-       m_player->setPlayerSAO(NULL);
-       m_env->removePlayer(m_player);
-}
-
-std::string PlayerSAO::getPropertyPacket()
-{
-       m_prop.is_visible = (true);
-       return generateSetPropertiesCommand(m_prop);
-}
-
-void PlayerSAO::setMaxSpeedOverride(const v3f &vel)
-{
-       if (m_max_speed_override_time == 0.0f)
-               m_max_speed_override = vel;
-       else
-               m_max_speed_override += vel;
-       if (m_player) {
-               float accel = MYMIN(m_player->movement_acceleration_default,
-                               m_player->movement_acceleration_air);
-               m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS;
-       }
-}
-
-bool PlayerSAO::checkMovementCheat()
-{
-       if (isAttached() || m_is_singleplayer ||
-                       g_settings->getBool("disable_anticheat")) {
-               m_last_good_position = m_base_position;
-               return false;
-       }
-
-       bool cheated = false;
-       /*
-               Check player movements
-
-               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. Altough a node-based alternative might be possible
-               too, and much more lightweight.
-       */
-
-       float override_max_H, override_max_V;
-       if (m_max_speed_override_time > 0.0f) {
-               override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z));
-               override_max_V = fabs(m_max_speed_override.Y);
-       } else {
-               override_max_H = override_max_V = 0.0f;
-       }
-
-       float player_max_walk = 0; // horizontal movement
-       float player_max_jump = 0; // vertical upwards movement
-
-       if (m_privs.count("fast") != 0)
-               player_max_walk = m_player->movement_speed_fast; // Fast speed
-       else
-               player_max_walk = m_player->movement_speed_walk; // Normal speed
-       player_max_walk *= m_physics_override_speed;
-       player_max_walk = MYMAX(player_max_walk, override_max_H);
-
-       player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
-       // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
-       //        until this can be verified correctly, tolerate higher jumping speeds
-       player_max_jump *= 2.0;
-       player_max_jump = MYMAX(player_max_jump, override_max_V);
-
-       // Don't divide by zero!
-       if (player_max_walk < 0.0001f)
-               player_max_walk = 0.0001f;
-       if (player_max_jump < 0.0001f)
-               player_max_jump = 0.0001f;
-
-       v3f diff = (m_base_position - m_last_good_position);
-       float d_vert = diff.Y;
-       diff.Y = 0;
-       float d_horiz = diff.getLength();
-       float required_time = d_horiz / player_max_walk;
-
-       // FIXME: Checking downwards movement is not easily possible currently,
-       //        the server could calculate speed differences to examine the gravity
-       if (d_vert > 0) {
-               // In certain cases (water, ladders) walking speed is applied vertically
-               float s = MYMAX(player_max_jump, player_max_walk);
-               required_time = MYMAX(required_time, d_vert / s);
-       }
-
-       if (m_move_pool.grab(required_time)) {
-               m_last_good_position = m_base_position;
-       } else {
-               const float LAG_POOL_MIN = 5.0;
-               float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
-               lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
-               if (m_time_from_last_teleport > lag_pool_max) {
-                       actionstream << "Player " << m_player->getName()
-                                       << " moved too fast; resetting position"
-                                       << std::endl;
-                       cheated = true;
-               }
-               setBasePosition(m_last_good_position);
-       }
-       return cheated;
-}
-
-bool PlayerSAO::getCollisionBox(aabb3f *toset) const
-{
-       //update collision box
-       toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
-       toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
-
-       toset->MinEdge += m_base_position;
-       toset->MaxEdge += m_base_position;
-       return true;
-}
-
-bool PlayerSAO::getSelectionBox(aabb3f *toset) const
-{
-       if (!m_prop.is_visible || !m_prop.pointable) {
-               return false;
-       }
-
-       toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
-       toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
-
-       return true;
-}
-
-float PlayerSAO::getZoomFOV() const
-{
-       return m_prop.zoom_fov;
-}
index 32ab922d66c4588008b4779b7a0866d66817ba0f..5387fd108445f4885cc8bc430e759178739cf805 100644 (file)
@@ -99,302 +99,3 @@ private:
        std::string m_current_texture_modifier = "";
 };
 
-/*
-       PlayerSAO needs some internals exposed.
-*/
-
-class LagPool
-{
-       float m_pool = 15.0f;
-       float m_max = 15.0f;
-public:
-       LagPool() = default;
-
-       void setMax(float new_max)
-       {
-               m_max = new_max;
-               if(m_pool > new_max)
-                       m_pool = new_max;
-       }
-
-       void add(float dtime)
-       {
-               m_pool -= dtime;
-               if(m_pool < 0)
-                       m_pool = 0;
-       }
-
-       void empty()
-       {
-               m_pool = m_max;
-       }
-
-       bool grab(float dtime)
-       {
-               if(dtime <= 0)
-                       return true;
-               if(m_pool + dtime > m_max)
-                       return false;
-               m_pool += dtime;
-               return true;
-       }
-};
-
-class RemotePlayer;
-
-class PlayerSAO : public UnitSAO
-{
-public:
-       PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
-                       bool is_singleplayer);
-
-       ActiveObjectType getType() const
-       { return ACTIVEOBJECT_TYPE_PLAYER; }
-       ActiveObjectType getSendType() const
-       { return ACTIVEOBJECT_TYPE_GENERIC; }
-       std::string getDescription();
-
-       /*
-               Active object <-> environment interface
-       */
-
-       void addedToEnvironment(u32 dtime_s);
-       void removingFromEnvironment();
-       bool isStaticAllowed() const { return false; }
-       std::string getClientInitializationData(u16 protocol_version);
-       void getStaticData(std::string *result) const;
-       void step(float dtime, bool send_recommended);
-       void setBasePosition(const v3f &position);
-       void setPos(const v3f &pos);
-       void moveTo(v3f pos, bool continuous);
-       void setPlayerYaw(const float yaw);
-       // Data should not be sent at player initialization
-       void setPlayerYawAndSend(const float yaw);
-       void setLookPitch(const float pitch);
-       // Data should not be sent at player initialization
-       void setLookPitchAndSend(const float pitch);
-       f32 getLookPitch() const { return m_pitch; }
-       f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; }
-       // Deprecated
-       f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; }
-       void setFov(const float pitch);
-       f32 getFov() const { return m_fov; }
-       void setWantedRange(const s16 range);
-       s16 getWantedRange() const { return m_wanted_range; }
-
-       /*
-               Interaction interface
-       */
-
-       u16 punch(v3f dir,
-               const ToolCapabilities *toolcap,
-               ServerActiveObject *puncher,
-               float time_from_last_punch);
-       void rightClick(ServerActiveObject *clicker) {}
-       void setHP(s32 hp, const PlayerHPChangeReason &reason);
-       void setHPRaw(u16 hp) { m_hp = hp; }
-       s16 readDamage();
-       u16 getBreath() const { return m_breath; }
-       void setBreath(const u16 breath, bool send = true);
-
-       /*
-               Inventory interface
-       */
-       Inventory *getInventory() const;
-       InventoryLocation getInventoryLocation() const;
-       void setInventoryModified() {}
-       std::string getWieldList() const { return "main"; }
-       u16 getWieldIndex() const;
-       ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const;
-       bool setWieldedItem(const ItemStack &item);
-
-       /*
-               PlayerSAO-specific
-       */
-
-       void disconnected();
-
-       RemotePlayer *getPlayer() { return m_player; }
-       session_t getPeerID() const { return m_peer_id; }
-
-       // Cheat prevention
-
-       v3f getLastGoodPosition() const
-       {
-               return m_last_good_position;
-       }
-       float resetTimeFromLastPunch()
-       {
-               float r = m_time_from_last_punch;
-               m_time_from_last_punch = 0.0;
-               return r;
-       }
-       void noCheatDigStart(const 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);
-       }
-       LagPool& getDigPool()
-       {
-               return m_dig_pool;
-       }
-       void setMaxSpeedOverride(const v3f &vel);
-       // Returns true if cheated
-       bool checkMovementCheat();
-
-       // Other
-
-       void updatePrivileges(const std::set<std::string> &privs,
-                       bool is_singleplayer)
-       {
-               m_privs = privs;
-               m_is_singleplayer = is_singleplayer;
-       }
-
-       bool getCollisionBox(aabb3f *toset) const;
-       bool getSelectionBox(aabb3f *toset) const;
-       bool collideWithObjects() const { return true; }
-
-       void finalize(RemotePlayer *player, const std::set<std::string> &privs);
-
-       v3f getEyePosition() const { return m_base_position + getEyeOffset(); }
-       v3f getEyeOffset() const;
-       float getZoomFOV() const;
-
-       inline Metadata &getMeta() { return m_meta; }
-
-private:
-       std::string getPropertyPacket();
-       void unlinkPlayerSessionAndSave();
-       std::string generateUpdatePhysicsOverrideCommand() const;
-
-       RemotePlayer *m_player = nullptr;
-       session_t m_peer_id = 0;
-
-       // Cheat prevention
-       LagPool m_dig_pool;
-       LagPool m_move_pool;
-       v3f m_last_good_position;
-       float m_time_from_last_teleport = 0.0f;
-       float m_time_from_last_punch = 0.0f;
-       v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
-       float m_nocheat_dig_time = 0.0f;
-       float m_max_speed_override_time = 0.0f;
-       v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f);
-
-       // Timers
-       IntervalLimiter m_breathing_interval;
-       IntervalLimiter m_drowning_interval;
-       IntervalLimiter m_node_hurt_interval;
-
-       bool m_position_not_sent = false;
-
-       // Cached privileges for enforcement
-       std::set<std::string> m_privs;
-       bool m_is_singleplayer;
-
-       u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
-       f32 m_pitch = 0.0f;
-       f32 m_fov = 0.0f;
-       s16 m_wanted_range = 0.0f;
-
-       Metadata m_meta;
-public:
-       float m_physics_override_speed = 1.0f;
-       float m_physics_override_jump = 1.0f;
-       float m_physics_override_gravity = 1.0f;
-       bool m_physics_override_sneak = true;
-       bool m_physics_override_sneak_glitch = false;
-       bool m_physics_override_new_move = true;
-       bool m_physics_override_sent = false;
-};
-
-
-struct PlayerHPChangeReason {
-       enum Type : u8 {
-               SET_HP,
-               PLAYER_PUNCH,
-               FALL,
-               NODE_DAMAGE,
-               DROWNING,
-               RESPAWN
-       };
-
-       Type type = SET_HP;
-       bool from_mod = false;
-       int lua_reference = -1;
-
-       // For PLAYER_PUNCH
-       ServerActiveObject *object = nullptr;
-       // For NODE_DAMAGE
-       std::string node;
-
-       inline bool hasLuaReference() const
-       {
-               return lua_reference >= 0;
-       }
-
-       bool setTypeFromString(const std::string &typestr)
-       {
-               if (typestr == "set_hp")
-                       type = SET_HP;
-               else if (typestr == "punch")
-                       type = PLAYER_PUNCH;
-               else if (typestr == "fall")
-                       type = FALL;
-               else if (typestr == "node_damage")
-                       type = NODE_DAMAGE;
-               else if (typestr == "drown")
-                       type = DROWNING;
-               else if (typestr == "respawn")
-                       type = RESPAWN;
-               else
-                       return false;
-
-               return true;
-       }
-
-       std::string getTypeAsString() const
-       {
-               switch (type) {
-               case PlayerHPChangeReason::SET_HP:
-                       return "set_hp";
-               case PlayerHPChangeReason::PLAYER_PUNCH:
-                       return "punch";
-               case PlayerHPChangeReason::FALL:
-                       return "fall";
-               case PlayerHPChangeReason::NODE_DAMAGE:
-                       return "node_damage";
-               case PlayerHPChangeReason::DROWNING:
-                       return "drown";
-               case PlayerHPChangeReason::RESPAWN:
-                       return "respawn";
-               default:
-                       return "?";
-               }
-       }
-
-       PlayerHPChangeReason(Type type):
-                       type(type)
-       {}
-
-       PlayerHPChangeReason(Type type, ServerActiveObject *object):
-                       type(type), object(object)
-       {}
-
-       PlayerHPChangeReason(Type type, std::string node):
-                       type(type), node(node)
-       {}
-};
index d09f1c07466ac78ea2081da8e2f8c2033eada980..d2b0b15431badc0ffa80503bcd852c6294df2d76 100644 (file)
@@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <cassert>
 #include <json/json.h>
 #include "database-files.h"
-#include "content_sao.h"
 #include "remoteplayer.h"
 #include "settings.h"
 #include "porting.h"
 #include "filesys.h"
+#include "server/player_sao.h"
 #include "util/string.h"
 
 // !!! WARNING !!!
index d7c94ff15b4ed52dcd0c5358e9d15a6c8aeae833..77385e2407a85fa93426dbe5c7ef0f6be99ca48d 100644 (file)
@@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "debug.h"
 #include "exceptions.h"
 #include "settings.h"
-#include "content_sao.h"
 #include "remoteplayer.h"
+#include "server/player_sao.h"
 
 Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
        m_connect_string(connect_string)
index 1bacdfe6c93be4d16f7c47144d7d41ebf88d7bcd..4560743b98e03ba78b2e39553fd272ea9a68edb0 100644 (file)
@@ -33,8 +33,8 @@ SQLite format specification:
 #include "settings.h"
 #include "porting.h"
 #include "util/string.h"
-#include "content_sao.h"
 #include "remoteplayer.h"
+#include "server/player_sao.h"
 
 #include <cassert>
 
index 23bcc867f7d93588a17908b27119ee3307e87564..b2fdb2a22a6f4f3551155873805b4e23057556b6 100644 (file)
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "chatmessage.h"
 #include "server.h"
 #include "log.h"
-#include "content_sao.h"
 #include "emerge.h"
 #include "mapblock.h"
 #include "modchannels.h"
@@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "network/connection.h"
 #include "network/networkprotocol.h"
 #include "network/serveropcodes.h"
+#include "server/player_sao.h"
 #include "util/auth.h"
 #include "util/base64.h"
 #include "util/pointedthing.h"
index 1a8fec68c20690fd078d1e09ceffd9f099b5ca7d..7a603d53eed7369b94773ebb17b0fced4b360f5d 100644 (file)
@@ -20,13 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "remoteplayer.h"
 #include <json/json.h>
-#include "content_sao.h"
 #include "filesys.h"
 #include "gamedef.h"
 #include "porting.h"  // strlcpy
 #include "server.h"
 #include "settings.h"
 #include "convert_json.h"
+#include "server/player_sao.h"
 
 /*
        RemotePlayer
index 60f12052fde5eee425ed63d340ac4add56772eec..c8cd7539fc460cc14c2f015a0ad7be9a17bef14b 100644 (file)
@@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_types.h"
 #include "nodedef.h"
 #include "object_properties.h"
-#include "content_sao.h"
 #include "cpp_api/s_node.h"
 #include "lua_api/l_object.h"
 #include "lua_api/l_item.h"
@@ -29,10 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include "log.h"
 #include "tool.h"
-#include "server/serveractiveobject.h"
 #include "porting.h"
 #include "mapgen/mg_schematic.h"
 #include "noise.h"
+#include "server/player_sao.h"
 #include "util/pointedthing.h"
 #include "debug.h" // For FATAL_ERROR
 #include <json/json.h>
index 16c20eeae2671dafbd80c82824542f7c7ba8c497..150baf77e3d5ab6d0f314f8754a67de380a29082 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "cpp_api/s_security.h"
 #include "lua_api/l_object.h"
 #include "common/c_converter.h"
-#include "server/serveractiveobject.h"
+#include "server/player_sao.h"
 #include "filesys.h"
 #include "content/mods.h"
 #include "porting.h"
index cf24ddc730ca4943c6433b59e7a6a2cd305fe425..7ca3d8f30c5b610ecaad0017a754963cca6e29fa 100644 (file)
@@ -28,6 +28,7 @@ struct InventoryLocation;
 struct ItemStack;
 struct ToolCapabilities;
 struct PlayerHPChangeReason;
+class ServerActiveObject;
 
 class ScriptApiPlayer : virtual public ScriptApiBase
 {
index 31e582d3d52333a57ed75b88c555d6d64b6bf6e5..438669feb5060120875a66c4c2ff1653d405661b 100644 (file)
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <algorithm>
 #include "lua_api/l_env.h"
 #include "lua_api/l_internal.h"
 #include "lua_api/l_nodemeta.h"
@@ -25,7 +26,6 @@ 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 "mapblock.h"
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "pathfinder.h"
 #include "face_position_cache.h"
 #include "remoteplayer.h"
+#include "server/player_sao.h"
 #ifndef SERVER
 #include "client/client.h"
 #endif
index 1ea144a1cd606268350b5e35297d7b023e077b56..95c96235e9e5a2cf2bf2ec1b75e9c48479e68457 100644 (file)
@@ -27,12 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_content.h"
 #include "log.h"
 #include "tool.h"
-#include "server/serveractiveobject.h"
 #include "content_sao.h"
 #include "remoteplayer.h"
 #include "server.h"
 #include "hud.h"
 #include "scripting_server.h"
+#include "server/player_sao.h"
 
 /*
        ObjectRef
index 529466f6b23cf6025a0056cf661c2ce23b100e0c..85d07fbc4e6c0b22e828037cebff18ec1c594bc3 100644 (file)
@@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapgen/mg_biome.h"
 #include "content_mapnode.h"
 #include "content_nodemeta.h"
-#include "content_sao.h"
 #include "content/mods.h"
 #include "modchannels.h"
 #include "serverlist.h"
@@ -64,6 +63,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "chatmessage.h"
 #include "chat_interface.h"
 #include "remoteplayer.h"
+#include "server/player_sao.h"
 
 class ClientNotFoundException : public BaseException
 {
index 9fa5ed9fac94c2002f69a640ee172db1268282d2..26eaed5ac17e36922ff728d4bb50a8d570c815d4 100644 (file)
@@ -1,6 +1,7 @@
 set(server_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp
        PARENT_SCOPE)
diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp
new file mode 100644 (file)
index 0000000..58fcea5
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2020 Minetest core developers & community
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "player_sao.h"
+#include "nodedef.h"
+#include "remoteplayer.h"
+#include "scripting_server.h"
+#include "server.h"
+#include "serverenvironment.h"
+
+PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
+               bool is_singleplayer):
+       UnitSAO(env_, v3f(0,0,0)),
+       m_player(player_),
+       m_peer_id(peer_id_),
+       m_is_singleplayer(is_singleplayer)
+{
+       SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT);
+
+       m_prop.hp_max = PLAYER_MAX_HP_DEFAULT;
+       m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT;
+       m_prop.physical = false;
+       m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
+       m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
+       m_prop.pointable = true;
+       // Start of default appearance, this should be overwritten by Lua
+       m_prop.visual = "upright_sprite";
+       m_prop.visual_size = v3f(1, 2, 1);
+       m_prop.textures.clear();
+       m_prop.textures.emplace_back("player.png");
+       m_prop.textures.emplace_back("player_back.png");
+       m_prop.colors.clear();
+       m_prop.colors.emplace_back(255, 255, 255, 255);
+       m_prop.spritediv = v2s16(1,1);
+       m_prop.eye_height = 1.625f;
+       // End of default appearance
+       m_prop.is_visible = true;
+       m_prop.backface_culling = false;
+       m_prop.makes_footstep_sound = true;
+       m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
+       m_hp = m_prop.hp_max;
+       m_breath = m_prop.breath_max;
+       // Disable zoom in survival mode using a value of 0
+       m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
+
+       if (!g_settings->getBool("enable_damage"))
+               m_armor_groups["immortal"] = 1;
+}
+
+void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
+{
+       assert(player);
+       m_player = player;
+       m_privs = privs;
+}
+
+v3f PlayerSAO::getEyeOffset() const
+{
+       return v3f(0, BS * m_prop.eye_height, 0);
+}
+
+std::string PlayerSAO::getDescription()
+{
+       return std::string("player ") + m_player->getName();
+}
+
+// Called after id has been set and has been inserted in environment
+void PlayerSAO::addedToEnvironment(u32 dtime_s)
+{
+       ServerActiveObject::addedToEnvironment(dtime_s);
+       ServerActiveObject::setBasePosition(m_base_position);
+       m_player->setPlayerSAO(this);
+       m_player->setPeerId(m_peer_id);
+       m_last_good_position = m_base_position;
+}
+
+// Called before removing from environment
+void PlayerSAO::removingFromEnvironment()
+{
+       ServerActiveObject::removingFromEnvironment();
+       if (m_player->getPlayerSAO() == this) {
+               unlinkPlayerSessionAndSave();
+               for (u32 attached_particle_spawner : m_attached_particle_spawners) {
+                       m_env->deleteParticleSpawner(attached_particle_spawner, false);
+               }
+       }
+}
+
+std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
+{
+       std::ostringstream os(std::ios::binary);
+
+       // Protocol >= 15
+       writeU8(os, 1); // version
+       os << serializeString(m_player->getName()); // name
+       writeU8(os, 1); // is_player
+       writeS16(os, getId()); // id
+       writeV3F32(os, m_base_position);
+       writeV3F32(os, m_rotation);
+       writeU16(os, getHP());
+
+       std::ostringstream msg_os(std::ios::binary);
+       msg_os << serializeLongString(getPropertyPacket()); // message 1
+       msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2
+       msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3
+       for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
+                       ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
+               msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first,
+                       (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
+       }
+       msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4
+       msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5
+       // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
+       msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6
+       int message_count = 6 + m_bone_position.size();
+       for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
+                       ii != m_attachment_child_ids.end(); ++ii) {
+               if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
+                       message_count++;
+                       msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version));
+               }
+       }
+
+       writeU8(os, message_count);
+       os.write(msg_os.str().c_str(), msg_os.str().size());
+
+       // return result
+       return os.str();
+}
+
+void PlayerSAO::getStaticData(std::string * result) const
+{
+       FATAL_ERROR("Obsolete function");
+}
+
+void PlayerSAO::step(float dtime, bool send_recommended)
+{
+       if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) {
+               // Get nose/mouth position, approximate with eye position
+               v3s16 p = floatToInt(getEyePosition(), BS);
+               MapNode n = m_env->getMap().getNode(p);
+               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
+               // If node generates drown
+               if (c.drowning > 0 && m_hp > 0) {
+                       if (m_breath > 0)
+                               setBreath(m_breath - 1);
+
+                       // No more breath, damage player
+                       if (m_breath == 0) {
+                               PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING);
+                               setHP(m_hp - c.drowning, reason);
+                               m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
+                       }
+               }
+       }
+
+       if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) {
+               // Get nose/mouth position, approximate with eye position
+               v3s16 p = floatToInt(getEyePosition(), BS);
+               MapNode n = m_env->getMap().getNode(p);
+               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
+               // If player is alive & not drowning & not in ignore & not immortal, breathe
+               if (m_breath < m_prop.breath_max && c.drowning == 0 &&
+                               n.getContent() != CONTENT_IGNORE && m_hp > 0)
+                       setBreath(m_breath + 1);
+       }
+
+       if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) {
+               u32 damage_per_second = 0;
+               std::string nodename;
+               // Lowest and highest damage points are 0.1 within collisionbox
+               float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f;
+
+               // Sequence of damage points, starting 0.1 above feet and progressing
+               // upwards in 1 node intervals, stopping below top damage point.
+               for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) {
+                       v3s16 p = floatToInt(m_base_position +
+                               v3f(0.0f, dam_height * BS, 0.0f), BS);
+                       MapNode n = m_env->getMap().getNode(p);
+                       const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
+                       if (c.damage_per_second > damage_per_second) {
+                               damage_per_second = c.damage_per_second;
+                               nodename = c.name;
+                       }
+               }
+
+               // Top damage point
+               v3s16 ptop = floatToInt(m_base_position +
+                       v3f(0.0f, dam_top * BS, 0.0f), BS);
+               MapNode ntop = m_env->getMap().getNode(ptop);
+               const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop);
+               if (c.damage_per_second > damage_per_second) {
+                       damage_per_second = c.damage_per_second;
+                       nodename = c.name;
+               }
+
+               if (damage_per_second != 0 && m_hp > 0) {
+                       s32 newhp = (s32)m_hp - (s32)damage_per_second;
+                       PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename);
+                       setHP(newhp, reason);
+                       m_env->getGameDef()->SendPlayerHPOrDie(this, reason);
+               }
+       }
+
+       if (!m_properties_sent) {
+               m_properties_sent = true;
+               std::string str = getPropertyPacket();
+               // create message and add to list
+               ActiveObjectMessage aom(getId(), true, str);
+               m_messages_out.push(aom);
+               m_env->getScriptIface()->player_event(this, "properties_changed");
+       }
+
+       // If attached, check that our parent is still there. If it isn't, detach.
+       if (m_attachment_parent_id && !isAttached()) {
+               m_attachment_parent_id = 0;
+               m_attachment_bone = "";
+               m_attachment_position = v3f(0.0f, 0.0f, 0.0f);
+               m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f);
+               setBasePosition(m_last_good_position);
+               m_env->getGameDef()->SendMovePlayer(m_peer_id);
+       }
+
+       //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
+
+       // Set lag pool maximums based on estimated lag
+       const float LAG_POOL_MIN = 5.0f;
+       float lag_pool_max = m_env->getMaxLagEstimate() * 2.0f;
+       if(lag_pool_max < LAG_POOL_MIN)
+               lag_pool_max = LAG_POOL_MIN;
+       m_dig_pool.setMax(lag_pool_max);
+       m_move_pool.setMax(lag_pool_max);
+
+       // Increment cheat prevention timers
+       m_dig_pool.add(dtime);
+       m_move_pool.add(dtime);
+       m_time_from_last_teleport += dtime;
+       m_time_from_last_punch += dtime;
+       m_nocheat_dig_time += dtime;
+       m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f);
+
+       // Each frame, parent position is copied if the object is attached,
+       // otherwise it's calculated normally.
+       // If the object gets detached this comes into effect automatically from
+       // the last known origin.
+       if (isAttached()) {
+               v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
+               m_last_good_position = pos;
+               setBasePosition(pos);
+       }
+
+       if (!send_recommended)
+               return;
+
+       if (m_position_not_sent) {
+               m_position_not_sent = false;
+               float update_interval = m_env->getSendRecommendedInterval();
+               v3f pos;
+               // When attached, the position is only sent to clients where the
+               // parent isn't known
+               if (isAttached())
+                       pos = m_last_good_position;
+               else
+                       pos = m_base_position;
+
+               std::string str = generateUpdatePositionCommand(
+                       pos,
+                       v3f(0.0f, 0.0f, 0.0f),
+                       v3f(0.0f, 0.0f, 0.0f),
+                       m_rotation,
+                       true,
+                       false,
+                       update_interval
+               );
+               // create message and add to list
+               m_messages_out.emplace(getId(), false, str);
+       }
+
+       if (!m_armor_groups_sent) {
+               m_armor_groups_sent = true;
+               // create message and add to list
+               m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand());
+       }
+
+       if (!m_physics_override_sent) {
+               m_physics_override_sent = true;
+               // create message and add to list
+               m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand());
+       }
+
+       if (!m_animation_sent) {
+               m_animation_sent = true;
+               // create message and add to list
+               m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand());
+       }
+
+       if (!m_bone_position_sent) {
+               m_bone_position_sent = true;
+               for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
+                               ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
+                       std::string str = generateUpdateBonePositionCommand((*ii).first,
+                                       (*ii).second.X, (*ii).second.Y);
+                       // create message and add to list
+                       m_messages_out.emplace(getId(), true, str);
+               }
+       }
+
+       if (!m_attachment_sent) {
+               m_attachment_sent = true;
+               std::string str = generateUpdateAttachmentCommand();
+               // create message and add to list
+               ActiveObjectMessage aom(getId(), true, str);
+               m_messages_out.push(aom);
+       }
+}
+
+std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const
+{
+       std::ostringstream os(std::ios::binary);
+       // command
+       writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE);
+       // parameters
+       writeF32(os, m_physics_override_speed);
+       writeF32(os, m_physics_override_jump);
+       writeF32(os, m_physics_override_gravity);
+       // these are sent inverted so we get true when the server sends nothing
+       writeU8(os, !m_physics_override_sneak);
+       writeU8(os, !m_physics_override_sneak_glitch);
+       writeU8(os, !m_physics_override_new_move);
+       return os.str();
+}
+
+void PlayerSAO::setBasePosition(const v3f &position)
+{
+       if (m_player && position != m_base_position)
+               m_player->setDirty(true);
+
+       // This needs to be ran for attachments too
+       ServerActiveObject::setBasePosition(position);
+
+       // Updating is not wanted/required for player migration
+       if (m_env) {
+               m_position_not_sent = true;
+       }
+}
+
+void PlayerSAO::setPos(const v3f &pos)
+{
+       if(isAttached())
+               return;
+
+       // Send mapblock of target location
+       v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE);
+       m_env->getGameDef()->SendBlock(m_peer_id, blockpos);
+
+       setBasePosition(pos);
+       // Movement caused by this command is always valid
+       m_last_good_position = pos;
+       m_move_pool.empty();
+       m_time_from_last_teleport = 0.0;
+       m_env->getGameDef()->SendMovePlayer(m_peer_id);
+}
+
+void PlayerSAO::moveTo(v3f pos, bool continuous)
+{
+       if(isAttached())
+               return;
+
+       setBasePosition(pos);
+       // Movement caused by this command is always valid
+       m_last_good_position = pos;
+       m_move_pool.empty();
+       m_time_from_last_teleport = 0.0;
+       m_env->getGameDef()->SendMovePlayer(m_peer_id);
+}
+
+void PlayerSAO::setPlayerYaw(const float yaw)
+{
+       v3f rotation(0, yaw, 0);
+       if (m_player && yaw != m_rotation.Y)
+               m_player->setDirty(true);
+
+       // Set player model yaw, not look view
+       UnitSAO::setRotation(rotation);
+}
+
+void PlayerSAO::setFov(const float fov)
+{
+       if (m_player && fov != m_fov)
+               m_player->setDirty(true);
+
+       m_fov = fov;
+}
+
+void PlayerSAO::setWantedRange(const s16 range)
+{
+       if (m_player && range != m_wanted_range)
+               m_player->setDirty(true);
+
+       m_wanted_range = range;
+}
+
+void PlayerSAO::setPlayerYawAndSend(const float yaw)
+{
+       setPlayerYaw(yaw);
+       m_env->getGameDef()->SendMovePlayer(m_peer_id);
+}
+
+void PlayerSAO::setLookPitch(const float pitch)
+{
+       if (m_player && pitch != m_pitch)
+               m_player->setDirty(true);
+
+       m_pitch = pitch;
+}
+
+void PlayerSAO::setLookPitchAndSend(const float pitch)
+{
+       setLookPitch(pitch);
+       m_env->getGameDef()->SendMovePlayer(m_peer_id);
+}
+
+u16 PlayerSAO::punch(v3f dir,
+       const ToolCapabilities *toolcap,
+       ServerActiveObject *puncher,
+       float time_from_last_punch)
+{
+       if (!toolcap)
+               return 0;
+
+       FATAL_ERROR_IF(!puncher, "Punch action called without SAO");
+
+       // No effect if PvP disabled or if immortal
+       if (isImmortal() || !g_settings->getBool("enable_pvp")) {
+               if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                       // create message and add to list
+                       sendPunchCommand();
+                       return 0;
+               }
+       }
+
+       s32 old_hp = getHP();
+       HitParams hitparams = getHitParams(m_armor_groups, toolcap,
+                       time_from_last_punch);
+
+       PlayerSAO *playersao = m_player->getPlayerSAO();
+
+       bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
+                               puncher, time_from_last_punch, toolcap, dir,
+                               hitparams.hp);
+
+       if (!damage_handled) {
+               setHP((s32)getHP() - (s32)hitparams.hp,
+                               PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
+       } else { // override client prediction
+               if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                       // create message and add to list
+                       sendPunchCommand();
+               }
+       }
+
+       actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
+               ", hp=" << puncher->getHP() << ") punched " <<
+               getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
+               "), damage=" << (old_hp - (s32)getHP()) <<
+               (damage_handled ? " (handled by Lua)" : "") << std::endl;
+
+       return hitparams.wear;
+}
+
+void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
+{
+       s32 oldhp = m_hp;
+
+       hp = rangelim(hp, 0, m_prop.hp_max);
+
+       if (oldhp != hp) {
+               s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
+               if (hp_change == 0)
+                       return;
+
+               hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max);
+       }
+
+       if (hp < oldhp && isImmortal())
+               return;
+
+       m_hp = hp;
+
+       // Update properties on death
+       if ((hp == 0) != (oldhp == 0))
+               m_properties_sent = false;
+}
+
+void PlayerSAO::setBreath(const u16 breath, bool send)
+{
+       if (m_player && breath != m_breath)
+               m_player->setDirty(true);
+
+       m_breath = rangelim(breath, 0, m_prop.breath_max);
+
+       if (send)
+               m_env->getGameDef()->SendPlayerBreath(this);
+}
+
+Inventory *PlayerSAO::getInventory() const
+{
+       return m_player ? &m_player->inventory : nullptr;
+}
+
+InventoryLocation PlayerSAO::getInventoryLocation() const
+{
+       InventoryLocation loc;
+       loc.setPlayer(m_player->getName());
+       return loc;
+}
+
+u16 PlayerSAO::getWieldIndex() const
+{
+       return m_player->getWieldIndex();
+}
+
+ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const
+{
+       return m_player->getWieldedItem(selected, hand);
+}
+
+bool PlayerSAO::setWieldedItem(const ItemStack &item)
+{
+       InventoryList *mlist = m_player->inventory.getList(getWieldList());
+       if (mlist) {
+               mlist->changeItem(m_player->getWieldIndex(), item);
+               return true;
+       }
+       return false;
+}
+
+void PlayerSAO::disconnected()
+{
+       m_peer_id = PEER_ID_INEXISTENT;
+       m_pending_removal = true;
+}
+
+void PlayerSAO::unlinkPlayerSessionAndSave()
+{
+       assert(m_player->getPlayerSAO() == this);
+       m_player->setPeerId(PEER_ID_INEXISTENT);
+       m_env->savePlayer(m_player);
+       m_player->setPlayerSAO(NULL);
+       m_env->removePlayer(m_player);
+}
+
+std::string PlayerSAO::getPropertyPacket()
+{
+       m_prop.is_visible = (true);
+       return generateSetPropertiesCommand(m_prop);
+}
+
+void PlayerSAO::setMaxSpeedOverride(const v3f &vel)
+{
+       if (m_max_speed_override_time == 0.0f)
+               m_max_speed_override = vel;
+       else
+               m_max_speed_override += vel;
+       if (m_player) {
+               float accel = MYMIN(m_player->movement_acceleration_default,
+                               m_player->movement_acceleration_air);
+               m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS;
+       }
+}
+
+bool PlayerSAO::checkMovementCheat()
+{
+       if (isAttached() || m_is_singleplayer ||
+                       g_settings->getBool("disable_anticheat")) {
+               m_last_good_position = m_base_position;
+               return false;
+       }
+
+       bool cheated = false;
+       /*
+               Check player movements
+
+               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. Altough a node-based alternative might be possible
+               too, and much more lightweight.
+       */
+
+       float override_max_H, override_max_V;
+       if (m_max_speed_override_time > 0.0f) {
+               override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z));
+               override_max_V = fabs(m_max_speed_override.Y);
+       } else {
+               override_max_H = override_max_V = 0.0f;
+       }
+
+       float player_max_walk = 0; // horizontal movement
+       float player_max_jump = 0; // vertical upwards movement
+
+       if (m_privs.count("fast") != 0)
+               player_max_walk = m_player->movement_speed_fast; // Fast speed
+       else
+               player_max_walk = m_player->movement_speed_walk; // Normal speed
+       player_max_walk *= m_physics_override_speed;
+       player_max_walk = MYMAX(player_max_walk, override_max_H);
+
+       player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
+       // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
+       //        until this can be verified correctly, tolerate higher jumping speeds
+       player_max_jump *= 2.0;
+       player_max_jump = MYMAX(player_max_jump, override_max_V);
+
+       // Don't divide by zero!
+       if (player_max_walk < 0.0001f)
+               player_max_walk = 0.0001f;
+       if (player_max_jump < 0.0001f)
+               player_max_jump = 0.0001f;
+
+       v3f diff = (m_base_position - m_last_good_position);
+       float d_vert = diff.Y;
+       diff.Y = 0;
+       float d_horiz = diff.getLength();
+       float required_time = d_horiz / player_max_walk;
+
+       // FIXME: Checking downwards movement is not easily possible currently,
+       //        the server could calculate speed differences to examine the gravity
+       if (d_vert > 0) {
+               // In certain cases (water, ladders) walking speed is applied vertically
+               float s = MYMAX(player_max_jump, player_max_walk);
+               required_time = MYMAX(required_time, d_vert / s);
+       }
+
+       if (m_move_pool.grab(required_time)) {
+               m_last_good_position = m_base_position;
+       } else {
+               const float LAG_POOL_MIN = 5.0;
+               float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
+               lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN);
+               if (m_time_from_last_teleport > lag_pool_max) {
+                       actionstream << "Player " << m_player->getName()
+                                       << " moved too fast; resetting position"
+                                       << std::endl;
+                       cheated = true;
+               }
+               setBasePosition(m_last_good_position);
+       }
+       return cheated;
+}
+
+bool PlayerSAO::getCollisionBox(aabb3f *toset) const
+{
+       //update collision box
+       toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
+       toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
+
+       toset->MinEdge += m_base_position;
+       toset->MaxEdge += m_base_position;
+       return true;
+}
+
+bool PlayerSAO::getSelectionBox(aabb3f *toset) const
+{
+       if (!m_prop.is_visible || !m_prop.pointable) {
+               return false;
+       }
+
+       toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
+       toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
+
+       return true;
+}
+
+float PlayerSAO::getZoomFOV() const
+{
+       return m_prop.zoom_fov;
+}
diff --git a/src/server/player_sao.h b/src/server/player_sao.h
new file mode 100644 (file)
index 0000000..ce1cb16
--- /dev/null
@@ -0,0 +1,325 @@
+
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2020 Minetest core developers & community
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "constants.h"
+#include "network/networkprotocol.h"
+#include "unit_sao.h"
+#include "util/numeric.h"
+
+/*
+       PlayerSAO needs some internals exposed.
+*/
+
+class LagPool
+{
+       float m_pool = 15.0f;
+       float m_max = 15.0f;
+public:
+       LagPool() = default;
+
+       void setMax(float new_max)
+       {
+               m_max = new_max;
+               if(m_pool > new_max)
+                       m_pool = new_max;
+       }
+
+       void add(float dtime)
+       {
+               m_pool -= dtime;
+               if(m_pool < 0)
+                       m_pool = 0;
+       }
+
+       void empty()
+       {
+               m_pool = m_max;
+       }
+
+       bool grab(float dtime)
+       {
+               if(dtime <= 0)
+                       return true;
+               if(m_pool + dtime > m_max)
+                       return false;
+               m_pool += dtime;
+               return true;
+       }
+};
+
+class RemotePlayer;
+
+class PlayerSAO : public UnitSAO
+{
+public:
+       PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
+                       bool is_singleplayer);
+
+       ActiveObjectType getType() const
+       { return ACTIVEOBJECT_TYPE_PLAYER; }
+       ActiveObjectType getSendType() const
+       { return ACTIVEOBJECT_TYPE_GENERIC; }
+       std::string getDescription();
+
+       /*
+               Active object <-> environment interface
+       */
+
+       void addedToEnvironment(u32 dtime_s);
+       void removingFromEnvironment();
+       bool isStaticAllowed() const { return false; }
+       std::string getClientInitializationData(u16 protocol_version);
+       void getStaticData(std::string *result) const;
+       void step(float dtime, bool send_recommended);
+       void setBasePosition(const v3f &position);
+       void setPos(const v3f &pos);
+       void moveTo(v3f pos, bool continuous);
+       void setPlayerYaw(const float yaw);
+       // Data should not be sent at player initialization
+       void setPlayerYawAndSend(const float yaw);
+       void setLookPitch(const float pitch);
+       // Data should not be sent at player initialization
+       void setLookPitchAndSend(const float pitch);
+       f32 getLookPitch() const { return m_pitch; }
+       f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; }
+       // Deprecated
+       f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; }
+       void setFov(const float pitch);
+       f32 getFov() const { return m_fov; }
+       void setWantedRange(const s16 range);
+       s16 getWantedRange() const { return m_wanted_range; }
+
+       /*
+               Interaction interface
+       */
+
+       u16 punch(v3f dir,
+               const ToolCapabilities *toolcap,
+               ServerActiveObject *puncher,
+               float time_from_last_punch);
+       void rightClick(ServerActiveObject *clicker) {}
+       void setHP(s32 hp, const PlayerHPChangeReason &reason);
+       void setHPRaw(u16 hp) { m_hp = hp; }
+       s16 readDamage();
+       u16 getBreath() const { return m_breath; }
+       void setBreath(const u16 breath, bool send = true);
+
+       /*
+               Inventory interface
+       */
+       Inventory *getInventory() const;
+       InventoryLocation getInventoryLocation() const;
+       void setInventoryModified() {}
+       std::string getWieldList() const { return "main"; }
+       u16 getWieldIndex() const;
+       ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const;
+       bool setWieldedItem(const ItemStack &item);
+
+       /*
+               PlayerSAO-specific
+       */
+
+       void disconnected();
+
+       RemotePlayer *getPlayer() { return m_player; }
+       session_t getPeerID() const { return m_peer_id; }
+
+       // Cheat prevention
+
+       v3f getLastGoodPosition() const
+       {
+               return m_last_good_position;
+       }
+       float resetTimeFromLastPunch()
+       {
+               float r = m_time_from_last_punch;
+               m_time_from_last_punch = 0.0;
+               return r;
+       }
+       void noCheatDigStart(const 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);
+       }
+       LagPool& getDigPool()
+       {
+               return m_dig_pool;
+       }
+       void setMaxSpeedOverride(const v3f &vel);
+       // Returns true if cheated
+       bool checkMovementCheat();
+
+       // Other
+
+       void updatePrivileges(const std::set<std::string> &privs,
+                       bool is_singleplayer)
+       {
+               m_privs = privs;
+               m_is_singleplayer = is_singleplayer;
+       }
+
+       bool getCollisionBox(aabb3f *toset) const;
+       bool getSelectionBox(aabb3f *toset) const;
+       bool collideWithObjects() const { return true; }
+
+       void finalize(RemotePlayer *player, const std::set<std::string> &privs);
+
+       v3f getEyePosition() const { return m_base_position + getEyeOffset(); }
+       v3f getEyeOffset() const;
+       float getZoomFOV() const;
+
+       inline Metadata &getMeta() { return m_meta; }
+
+private:
+       std::string getPropertyPacket();
+       void unlinkPlayerSessionAndSave();
+       std::string generateUpdatePhysicsOverrideCommand() const;
+
+       RemotePlayer *m_player = nullptr;
+       session_t m_peer_id = 0;
+
+       // Cheat prevention
+       LagPool m_dig_pool;
+       LagPool m_move_pool;
+       v3f m_last_good_position;
+       float m_time_from_last_teleport = 0.0f;
+       float m_time_from_last_punch = 0.0f;
+       v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
+       float m_nocheat_dig_time = 0.0f;
+       float m_max_speed_override_time = 0.0f;
+       v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f);
+
+       // Timers
+       IntervalLimiter m_breathing_interval;
+       IntervalLimiter m_drowning_interval;
+       IntervalLimiter m_node_hurt_interval;
+
+       bool m_position_not_sent = false;
+
+       // Cached privileges for enforcement
+       std::set<std::string> m_privs;
+       bool m_is_singleplayer;
+
+       u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
+       f32 m_pitch = 0.0f;
+       f32 m_fov = 0.0f;
+       s16 m_wanted_range = 0.0f;
+
+       Metadata m_meta;
+public:
+       float m_physics_override_speed = 1.0f;
+       float m_physics_override_jump = 1.0f;
+       float m_physics_override_gravity = 1.0f;
+       bool m_physics_override_sneak = true;
+       bool m_physics_override_sneak_glitch = false;
+       bool m_physics_override_new_move = true;
+       bool m_physics_override_sent = false;
+};
+
+
+struct PlayerHPChangeReason {
+       enum Type : u8 {
+               SET_HP,
+               PLAYER_PUNCH,
+               FALL,
+               NODE_DAMAGE,
+               DROWNING,
+               RESPAWN
+       };
+
+       Type type = SET_HP;
+       bool from_mod = false;
+       int lua_reference = -1;
+
+       // For PLAYER_PUNCH
+       ServerActiveObject *object = nullptr;
+       // For NODE_DAMAGE
+       std::string node;
+
+       inline bool hasLuaReference() const
+       {
+               return lua_reference >= 0;
+       }
+
+       bool setTypeFromString(const std::string &typestr)
+       {
+               if (typestr == "set_hp")
+                       type = SET_HP;
+               else if (typestr == "punch")
+                       type = PLAYER_PUNCH;
+               else if (typestr == "fall")
+                       type = FALL;
+               else if (typestr == "node_damage")
+                       type = NODE_DAMAGE;
+               else if (typestr == "drown")
+                       type = DROWNING;
+               else if (typestr == "respawn")
+                       type = RESPAWN;
+               else
+                       return false;
+
+               return true;
+       }
+
+       std::string getTypeAsString() const
+       {
+               switch (type) {
+               case PlayerHPChangeReason::SET_HP:
+                       return "set_hp";
+               case PlayerHPChangeReason::PLAYER_PUNCH:
+                       return "punch";
+               case PlayerHPChangeReason::FALL:
+                       return "fall";
+               case PlayerHPChangeReason::NODE_DAMAGE:
+                       return "node_damage";
+               case PlayerHPChangeReason::DROWNING:
+                       return "drown";
+               case PlayerHPChangeReason::RESPAWN:
+                       return "respawn";
+               default:
+                       return "?";
+               }
+       }
+
+       PlayerHPChangeReason(Type type):
+                       type(type)
+       {}
+
+       PlayerHPChangeReason(Type type, ServerActiveObject *object):
+                       type(type), object(object)
+       {}
+
+       PlayerHPChangeReason(Type type, std::string node):
+                       type(type), node(node)
+       {}
+};
index b30b7a76b5fed9a87074d991d998468a8435555d..74b0508b8c1a01003ff0b794ea70e54b7f96a0e4 100644 (file)
@@ -22,10 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "scripting_server.h"
 #include "serverenvironment.h"
 
-/*
-       UnitSAO
- */
-
 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos)
 {
        // Initialize something to armor groups
index 0ccbd772b7bed978bc94b4de1c1ef43c239a0778..c2ab5c07d60c1a5d7d182b0d858c7e29323cb155 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.
 */
 
+#include <algorithm>
 #include "serverenvironment.h"
-#include "content_sao.h"
 #include "settings.h"
 #include "log.h"
 #include "mapblock.h"
@@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #if USE_POSTGRESQL
 #include "database/database-postgresql.h"
 #endif
-#include <algorithm>
+#include "server/player_sao.h"
 
 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"