+void PlayerSAO::step(float dtime, bool send_recommended)
+{
+ 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_back(aom);
+ }
+
+ m_time_from_last_punch += dtime;
+
+ /*
+ 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.
+ */
+
+ //float player_max_speed = BS * 4.0; // Normal speed
+ float player_max_speed = BS * 20; // Fast speed
+ float player_max_speed_up = BS * 20;
+ player_max_speed *= 2.5; // Tolerance
+ player_max_speed_up *= 2.5;
+
+ m_last_good_position_age += dtime;
+ if(m_last_good_position_age >= 1.0){
+ float age = m_last_good_position_age;
+ v3f diff = (m_player->getPosition() - m_last_good_position);
+ float d_vert = diff.Y;
+ diff.Y = 0;
+ float d_horiz = diff.getLength();
+ /*infostream<<m_player->getName()<<"'s horizontal speed is "
+ <<(d_horiz/age)<<std::endl;*/
+ if(d_horiz <= age * player_max_speed &&
+ (d_vert < 0 || d_vert < age * player_max_speed_up)){
+ m_last_good_position = m_player->getPosition();
+ } else {
+ actionstream<<"Player "<<m_player->getName()
+ <<" moved too fast; resetting position"
+ <<std::endl;
+ m_player->setPosition(m_last_good_position);
+ m_teleported = true;
+ }
+ m_last_good_position_age = 0;
+ }
+
+ if(send_recommended == false)
+ return;
+
+ if(m_position_not_sent)
+ {
+ m_position_not_sent = false;
+ float update_interval = m_env->getSendRecommendedInterval();
+ std::string str = gob_cmd_update_position(
+ m_player->getPosition() + v3f(0,BS*1,0),
+ v3f(0,0,0),
+ v3f(0,0,0),
+ m_player->getYaw(),
+ true,
+ false,
+ update_interval
+ );
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), false, str);
+ m_messages_out.push_back(aom);
+ }
+
+ if(m_wielded_item_not_sent)
+ {
+ m_wielded_item_not_sent = false;
+ // GenericCAO has no special way to show this
+ }
+
+ if(m_armor_groups_sent == false){
+ m_armor_groups_sent = true;
+ std::string str = gob_cmd_update_armor_groups(
+ m_armor_groups);
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push_back(aom);
+ }
+}
+
+void PlayerSAO::setBasePosition(const v3f &position)
+{
+ ServerActiveObject::setBasePosition(position);
+ m_position_not_sent = true;
+}
+
+void PlayerSAO::setPos(v3f pos)
+{
+ m_player->setPosition(pos);
+ // Movement caused by this command is always valid
+ m_last_good_position = pos;
+ m_last_good_position_age = 0;
+ // Force position change on client
+ m_teleported = true;
+}
+
+void PlayerSAO::moveTo(v3f pos, bool continuous)
+{
+ m_player->setPosition(pos);
+ // Movement caused by this command is always valid
+ m_last_good_position = pos;
+ m_last_good_position_age = 0;
+ // Force position change on client
+ m_teleported = true;
+}
+
+int PlayerSAO::punch(v3f dir,
+ const ToolCapabilities *toolcap,
+ ServerActiveObject *puncher,
+ float time_from_last_punch)
+{
+ if(!toolcap)
+ return 0;
+
+ // No effect if PvP disabled
+ if(g_settings->getBool("enable_pvp") == false){
+ if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
+ std::string str = gob_cmd_punched(0, getHP());
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push_back(aom);
+ return 0;
+ }
+ }
+
+ HitParams hitparams = getHitParams(m_armor_groups, toolcap,
+ time_from_last_punch);
+
+ actionstream<<"Player "<<m_player->getName()<<" punched by "
+ <<puncher->getDescription()<<", damage "<<hitparams.hp
+ <<" HP"<<std::endl;
+
+ setHP(getHP() - hitparams.hp);
+
+ if(hitparams.hp != 0)
+ {
+ std::string str = gob_cmd_punched(hitparams.hp, getHP());
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push_back(aom);
+ }
+
+ return hitparams.wear;
+}
+
+void PlayerSAO::rightClick(ServerActiveObject *clicker)
+{
+}
+
+s16 PlayerSAO::getHP() const
+{
+ return m_player->hp;
+}
+
+void PlayerSAO::setHP(s16 hp)
+{
+ s16 oldhp = m_player->hp;
+
+ if(hp < 0)
+ hp = 0;
+ else if(hp > PLAYER_MAX_HP)
+ hp = PLAYER_MAX_HP;
+
+ if(hp < oldhp && g_settings->getBool("enable_damage") == false)
+ {
+ m_hp_not_sent = true; // fix wrong prediction on client
+ return;
+ }
+
+ m_player->hp = hp;
+
+ if(hp != oldhp)
+ m_hp_not_sent = true;
+
+ // On death or reincarnation send an active object message
+ if((hp == 0) != (oldhp == 0))
+ {
+ // Will send new is_visible value based on (getHP()!=0)
+ m_properties_sent = false;
+ // Send new HP
+ std::string str = gob_cmd_punched(0, getHP());
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push_back(aom);
+ }
+}
+
+void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
+{
+ m_armor_groups = armor_groups;
+ m_armor_groups_sent = false;
+}
+
+Inventory* PlayerSAO::getInventory()
+{
+ return m_inventory;
+}
+const Inventory* PlayerSAO::getInventory() const
+{
+ return m_inventory;
+}
+
+InventoryLocation PlayerSAO::getInventoryLocation() const
+{
+ InventoryLocation loc;
+ loc.setPlayer(m_player->getName());
+ return loc;
+}
+
+void PlayerSAO::setInventoryModified()
+{
+ m_inventory_not_sent = true;
+}
+
+std::string PlayerSAO::getWieldList() const
+{
+ return "main";
+}
+
+int PlayerSAO::getWieldIndex() const
+{
+ return m_wield_index;