]> git.lizzy.rs Git - minetest.git/blobdiff - src/content_sao.cpp
Allow the LUA API to set animations to meshes as well as the animation speed. Also...
[minetest.git] / src / content_sao.cpp
index 568e4b1da67cf7681636de9a07ec532fc5255776..6c2abf8f6b3a7e91f253dbed26ff7e90ae945a73 100644 (file)
@@ -3,16 +3,16 @@ Minetest-c55
 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+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 General Public License for more details.
+GNU Lesser General Public License for more details.
 
-You should have received a copy of the GNU General Public License along
+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.
 */
@@ -26,6 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h" // For compressZlib
 #include "tool.h" // For ToolCapabilities
 #include "gamedef.h"
+#include "player.h"
+#include "scriptapi.h"
+#include "genericobject.h"
+#include "util/serialize.h"
 
 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
 
@@ -138,6 +142,9 @@ TestSAO proto_TestSAO(NULL, v3f(0,0,0));
 
 /*
        ItemSAO
+
+       DEPRECATED: New dropped items are implemented in Lua; see
+                   builtin/item_entity.lua.
 */
 
 class ItemSAO : public ServerActiveObject
@@ -199,9 +206,12 @@ class ItemSAO : public ServerActiveObject
                        m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
                v3f pos_f = getBasePosition();
                v3f pos_f_old = pos_f;
+               v3f accel_f = v3f(0,0,0);
+               f32 stepheight = 0;
                IGameDef *gamedef = m_env->getGameDef();
                moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
-                               pos_max_d, box, dtime, pos_f, m_speed_f);
+                               pos_max_d, box, stepheight, dtime,
+                               pos_f, m_speed_f, accel_f);
                
                if(send_recommended == false)
                        return;
@@ -282,13 +292,6 @@ class ItemSAO : public ServerActiveObject
                        ServerActiveObject *puncher,
                        float time_from_last_punch)
        {
-               // Directly delete item in creative mode
-               if(g_settings->getBool("creative_mode") == true)
-               {
-                       m_removed = true;
-                       return 0;
-               }
-               
                // Take item into inventory
                ItemStack item = createItemStack();
                Inventory *inv = puncher->getInventory();
@@ -333,9 +336,6 @@ ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
        LuaEntitySAO
 */
 
-#include "scriptapi.h"
-#include "luaentity_common.h"
-
 // Prototype (registers item for deserialization)
 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
 
@@ -345,11 +345,11 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
        m_init_name(name),
        m_init_state(state),
        m_registered(false),
-       m_prop(new LuaEntityProperties),
        m_hp(-1),
        m_velocity(0,0,0),
        m_acceleration(0,0,0),
        m_yaw(0),
+       m_properties_sent(true),
        m_last_sent_yaw(0),
        m_last_sent_position(0,0,0),
        m_last_sent_velocity(0,0,0),
@@ -374,49 +374,50 @@ LuaEntitySAO::~LuaEntitySAO()
                lua_State *L = m_env->getLua();
                scriptapi_luaentity_rm(L, m_id);
        }
-       delete m_prop;
 }
 
-void LuaEntitySAO::addedToEnvironment()
+void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
 {
-       ServerActiveObject::addedToEnvironment();
+       ServerActiveObject::addedToEnvironment(dtime_s);
        
-       // Create entity from name and state
+       // Create entity from name
        lua_State *L = m_env->getLua();
-       m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str(),
-                       m_init_state.c_str());
+       m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
        
        if(m_registered){
                // Get properties
-               scriptapi_luaentity_get_properties(L, m_id, m_prop);
+               scriptapi_luaentity_get_properties(L, m_id, &m_prop);
+               // Initialize HP from properties
+               m_hp = m_prop.hp_max;
+               // Activate entity, supplying serialized state
+               scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
        }
 }
 
 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
                const std::string &data)
 {
-       std::istringstream is(data, std::ios::binary);
-       // read version
-       u8 version = readU8(is);
        std::string name;
        std::string state;
        s16 hp = 1;
        v3f velocity;
        float yaw = 0;
-       // check if version is supported
-       if(version == 0){
-               name = deSerializeString(is);
-               state = deSerializeLongString(is);
-       }
-       else if(version == 1){
-               name = deSerializeString(is);
-               state = deSerializeLongString(is);
-               hp = readS16(is);
-               velocity = readV3F1000(is);
-               yaw = readF1000(is);
-       }
-       else{
-               return NULL;
+       if(data != ""){
+               std::istringstream is(data, std::ios::binary);
+               // read version
+               u8 version = readU8(is);
+               // check if version is supported
+               if(version == 0){
+                       name = deSerializeString(is);
+                       state = deSerializeLongString(is);
+               }
+               else if(version == 1){
+                       name = deSerializeString(is);
+                       state = deSerializeLongString(is);
+                       hp = readS16(is);
+                       velocity = readV3F1000(is);
+                       yaw = readF1000(is);
+               }
        }
        // create object
        infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
@@ -430,24 +431,35 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
 
 void LuaEntitySAO::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_last_sent_position_timer += dtime;
        
-       if(m_prop->physical){
-               core::aabbox3d<f32> box = m_prop->collisionbox;
+       if(m_prop.physical){
+               core::aabbox3d<f32> box = m_prop.collisionbox;
                box.MinEdge *= BS;
                box.MaxEdge *= BS;
                collisionMoveResult moveresult;
                f32 pos_max_d = BS*0.25; // Distance per iteration
-               v3f p_pos = getBasePosition();
+               f32 stepheight = 0; // Maximum climbable step height
+               v3f p_pos = m_base_position;
                v3f p_velocity = m_velocity;
+               v3f p_acceleration = m_acceleration;
                IGameDef *gamedef = m_env->getGameDef();
-               moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
-                               pos_max_d, box, dtime, p_pos, p_velocity);
+               moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
+                               pos_max_d, box, stepheight, dtime,
+                               p_pos, p_velocity, p_acceleration);
                // Apply results
-               setBasePosition(p_pos);
+               m_base_position = p_pos;
                m_velocity = p_velocity;
-
-               m_velocity += dtime * m_acceleration;
+               m_acceleration = p_acceleration;
        } else {
                m_base_position += dtime * m_velocity + 0.5 * dtime
                                * dtime * m_acceleration;
@@ -479,16 +491,10 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 
        if(m_armor_groups_sent == false){
                m_armor_groups_sent = true;
-               std::ostringstream os(std::ios::binary);
-               writeU8(os, LUAENTITY_CMD_UPDATE_ARMOR_GROUPS);
-               writeU16(os, m_armor_groups.size());
-               for(ItemGroupList::const_iterator i = m_armor_groups.begin();
-                               i != m_armor_groups.end(); i++){
-                       os<<serializeString(i->first);
-                       writeS16(os, i->second);
-               }
+               std::string str = gob_cmd_update_armor_groups(
+                               m_armor_groups);
                // create message and add to list
-               ActiveObjectMessage aom(getId(), true, os.str());
+               ActiveObjectMessage aom(getId(), true, str);
                m_messages_out.push_back(aom);
        }
 }
@@ -496,25 +502,22 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 std::string LuaEntitySAO::getClientInitializationData()
 {
        std::ostringstream os(std::ios::binary);
-       // version
-       writeU8(os, 1);
-       // pos
+       writeU8(os, 0); // version
+       os<<serializeString(""); // name
+       writeU8(os, 0); // is_player
        writeV3F1000(os, m_base_position);
-       // yaw
        writeF1000(os, m_yaw);
-       // hp
        writeS16(os, m_hp);
-       // properties
-       std::ostringstream prop_os(std::ios::binary);
-       m_prop->serialize(prop_os);
-       os<<serializeLongString(prop_os.str());
+       writeU8(os, 2); // number of messages stuffed in here
+       os<<serializeLongString(getPropertyPacket()); // message 1
+       os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
        // return result
        return os.str();
 }
 
 std::string LuaEntitySAO::getStaticData()
 {
-       infostream<<__FUNCTION_NAME<<std::endl;
+       verbosestream<<__FUNCTION_NAME<<std::endl;
        std::ostringstream os(std::ios::binary);
        // version
        writeU8(os, 1);
@@ -570,17 +573,14 @@ int LuaEntitySAO::punch(v3f dir,
                                <<" hp, health now "<<getHP()<<" hp"<<std::endl;
                
                {
-                       std::ostringstream os(std::ios::binary);
-                       // command 
-                       writeU8(os, LUAENTITY_CMD_PUNCHED);
-                       // damage
-                       writeS16(os, result.damage);
-                       // result_hp
-                       writeS16(os, getHP());
+                       std::string str = gob_cmd_punched(result.damage, getHP());
                        // create message and add to list
-                       ActiveObjectMessage aom(getId(), true, os.str());
+                       ActiveObjectMessage aom(getId(), true, str);
                        m_messages_out.push_back(aom);
                }
+
+               if(getHP() == 0)
+                       m_removed = true;
        }
 
        lua_State *L = m_env->getLua();
@@ -598,17 +598,6 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
        scriptapi_luaentity_rightclick(L, m_id, clicker);
 }
 
-void LuaEntitySAO::setHP(s16 hp)
-{
-       if(hp < 0) hp = 0;
-       m_hp = hp;
-}
-
-s16 LuaEntitySAO::getHP()
-{
-       return m_hp;
-}
-
 void LuaEntitySAO::setPos(v3f pos)
 {
        m_base_position = pos;
@@ -635,7 +624,34 @@ std::string LuaEntitySAO::getDescription()
        os<<(m_base_position.Y/BS)<<",";
        os<<(m_base_position.Z/BS);
        os<<")";
-       return std::string("LuaEntitySAO");
+       return os.str();
+}
+
+void LuaEntitySAO::setHP(s16 hp)
+{
+       if(hp < 0) hp = 0;
+       m_hp = hp;
+}
+
+s16 LuaEntitySAO::getHP() const
+{
+       return m_hp;
+}
+
+void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
+{
+       m_armor_groups = armor_groups;
+       m_armor_groups_sent = false;
+}
+
+ObjectProperties* LuaEntitySAO::accessObjectProperties()
+{
+       return &m_prop;
+}
+
+void LuaEntitySAO::notifyObjectPropertiesModified()
+{
+       m_properties_sent = false;
 }
 
 void LuaEntitySAO::setVelocity(v3f velocity)
@@ -670,29 +686,23 @@ float LuaEntitySAO::getYaw()
 
 void LuaEntitySAO::setTextureMod(const std::string &mod)
 {
-       std::ostringstream os(std::ios::binary);
-       // command 
-       writeU8(os, LUAENTITY_CMD_SET_TEXTURE_MOD);
-       // parameters
-       os<<serializeString(mod);
+       std::string str = gob_cmd_set_texture_mod(mod);
        // create message and add to list
-       ActiveObjectMessage aom(getId(), false, os.str());
+       ActiveObjectMessage aom(getId(), true, str);
        m_messages_out.push_back(aom);
 }
 
 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
                bool select_horiz_by_yawpitch)
 {
-       std::ostringstream os(std::ios::binary);
-       // command
-       writeU8(os, LUAENTITY_CMD_SET_SPRITE);
-       // parameters
-       writeV2S16(os, p);
-       writeU16(os, num_frames);
-       writeF1000(os, framelength);
-       writeU8(os, select_horiz_by_yawpitch);
+       std::string str = gob_cmd_set_sprite(
+               p,
+               num_frames,
+               framelength,
+               select_horiz_by_yawpitch
+       );
        // create message and add to list
-       ActiveObjectMessage aom(getId(), false, os.str());
+       ActiveObjectMessage aom(getId(), true, str);
        m_messages_out.push_back(aom);
 }
 
@@ -701,10 +711,9 @@ std::string LuaEntitySAO::getName()
        return m_init_name;
 }
 
-void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
+std::string LuaEntitySAO::getPropertyPacket()
 {
-       m_armor_groups = armor_groups;
-       m_armor_groups_sent = false;
+       return gob_cmd_set_properties(m_prop);
 }
 
 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
@@ -719,27 +728,419 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
 
        float update_interval = m_env->getSendRecommendedInterval();
 
+       std::string str = gob_cmd_update_position(
+               m_base_position,
+               m_velocity,
+               m_acceleration,
+               m_yaw,
+               do_interpolate,
+               is_movement_end,
+               update_interval
+       );
+       // create message and add to list
+       ActiveObjectMessage aom(getId(), false, str);
+       m_messages_out.push_back(aom);
+}
+
+/*
+       PlayerSAO
+*/
+
+// No prototype, PlayerSAO does not need to be deserialized
+
+PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
+               const std::set<std::string> &privs, bool is_singleplayer):
+       ServerActiveObject(env_, v3f(0,0,0)),
+       m_player(player_),
+       m_peer_id(peer_id_),
+       m_inventory(NULL),
+       m_last_good_position(0,0,0),
+       m_last_good_position_age(0),
+       m_time_from_last_punch(0),
+       m_nocheat_dig_pos(32767, 32767, 32767),
+       m_nocheat_dig_time(0),
+       m_wield_index(0),
+       m_position_not_sent(false),
+       m_armor_groups_sent(false),
+       m_properties_sent(true),
+       m_privs(privs),
+       m_is_singleplayer(is_singleplayer),
+       // public
+       m_teleported(false),
+       m_inventory_not_sent(false),
+       m_hp_not_sent(false),
+       m_wielded_item_not_sent(false)
+{
+       assert(m_player);
+       assert(m_peer_id != 0);
+       setBasePosition(m_player->getPosition());
+       m_inventory = &m_player->inventory;
+       m_armor_groups["choppy"] = 2;
+       m_armor_groups["fleshy"] = 3;
+
+       m_prop.hp_max = PLAYER_MAX_HP;
+       m_prop.physical = false;
+       m_prop.weight = 75;
+       m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
+       // start of default appearance, this should be overwritten by LUA
+       m_prop.visual = "upright_sprite";
+       m_prop.visual_size = v2f(1, 2);
+       m_prop.textures.clear();
+       m_prop.textures.push_back("player.png");
+       m_prop.textures.push_back("player_back.png");
+       m_prop.spritediv = v2s16(1,1);
+       // end of default appearance
+       m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
+       m_prop.makes_footstep_sound = true;
+}
+
+PlayerSAO::~PlayerSAO()
+{
+       if(m_inventory != &m_player->inventory)
+               delete m_inventory;
+
+}
+
+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_player->getPosition());
+       m_player->setPlayerSAO(this);
+       m_player->peer_id = m_peer_id;
+       m_last_good_position = m_player->getPosition();
+       m_last_good_position_age = 0.0;
+}
+
+// Called before removing from environment
+void PlayerSAO::removingFromEnvironment()
+{
+       ServerActiveObject::removingFromEnvironment();
+       if(m_player->getPlayerSAO() == this)
+       {
+               m_player->setPlayerSAO(NULL);
+               m_player->peer_id = 0;
+       }
+}
+
+bool PlayerSAO::isStaticAllowed() const
+{
+       return false;
+}
+
+bool PlayerSAO::unlimitedTransferDistance() const
+{
+       return g_settings->getBool("unlimited_player_transfer_distance");
+}
+
+std::string PlayerSAO::getClientInitializationData()
+{
        std::ostringstream os(std::ios::binary);
-       // command
-       writeU8(os, LUAENTITY_CMD_UPDATE_POSITION);
+       writeU8(os, 0); // version
+       os<<serializeString(m_player->getName()); // name
+       writeU8(os, 1); // is_player
+       writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
+       writeF1000(os, m_player->getYaw());
+       writeS16(os, getHP());
+       writeU8(os, 2); // number of messages stuffed in here
+       os<<serializeLongString(getPropertyPacket()); // message 1
+       os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
+       return os.str();
+}
 
-       // do_interpolate
-       writeU8(os, do_interpolate);
-       // pos
-       writeV3F1000(os, m_base_position);
-       // velocity
-       writeV3F1000(os, m_velocity);
-       // acceleration
-       writeV3F1000(os, m_acceleration);
-       // yaw
-       writeF1000(os, m_yaw);
-       // is_end_position (for interpolation)
-       writeU8(os, is_movement_end);
-       // update_interval (for interpolation)
-       writeF1000(os, update_interval);
+std::string PlayerSAO::getStaticData()
+{
+       assert(0);
+       return "";
+}
 
-       // create message and add to list
-       ActiveObjectMessage aom(getId(), false, os.str());
-       m_messages_out.push_back(aom);
+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;
+       m_nocheat_dig_time += dtime;
+       
+       if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
+       {
+               m_last_good_position = m_player->getPosition();
+               m_last_good_position_age = 0;
+       }
+       else
+       {
+               /*
+                       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 player_max_speed = 0;
+               float player_max_speed_up = 0;
+               if(m_privs.count("fast") != 0){
+                       // Fast speed
+                       player_max_speed = BS * 20;
+                       player_max_speed_up = BS * 20;
+               } else {
+                       // Normal speed
+                       player_max_speed = BS * 4.0;
+                       player_max_speed_up = BS * 4.0;
+               }
+               // Tolerance
+               player_max_speed *= 2.5;
+               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;
+}
+
+ObjectProperties* PlayerSAO::accessObjectProperties()
+{
+       return &m_prop;
+}
+
+void PlayerSAO::notifyObjectPropertiesModified()
+{
+       m_properties_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;
+}
+
+void PlayerSAO::setWieldIndex(int i)
+{
+       if(i != m_wield_index)
+       {
+               m_wield_index = i;
+               m_wielded_item_not_sent = true;
+       }
+}
+
+void PlayerSAO::disconnected()
+{
+       m_peer_id = 0;
+       m_removed = true;
+       if(m_player->getPlayerSAO() == this)
+       {
+               m_player->setPlayerSAO(NULL);
+               m_player->peer_id = 0;
+       }
+}
+
+std::string PlayerSAO::getPropertyPacket()
+{
+       m_prop.is_visible = (getHP() != 0);
+       return gob_cmd_set_properties(m_prop);
 }