3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
27 #include "serialization.h" // For compressZlib
28 #include "tool.h" // For ToolCapabilities
32 #include "scripting_game.h"
33 #include "genericobject.h"
36 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
42 class TestSAO : public ServerActiveObject
45 TestSAO(ServerEnvironment *env, v3f pos):
46 ServerActiveObject(env, pos),
50 ServerActiveObject::registerType(getType(), create);
52 ActiveObjectType getType() const
53 { return ACTIVEOBJECT_TYPE_TEST; }
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new TestSAO(env, pos);
61 void step(float dtime, bool send_recommended)
70 m_base_position.Y += dtime * BS * 2;
71 if(m_base_position.Y > 8*BS)
72 m_base_position.Y = 2*BS;
74 if(send_recommended == false)
84 data += itos(0); // 0 = position
86 data += itos(m_base_position.X);
88 data += itos(m_base_position.Y);
90 data += itos(m_base_position.Z);
92 ActiveObjectMessage aom(getId(), false, data);
93 m_messages_out.push(aom);
97 bool getCollisionBox(aabb3f *toset) {
101 bool collideWithObjects() {
110 // Prototype (registers item for deserialization)
111 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
117 // Prototype (registers item for deserialization)
118 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
120 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
121 const std::string &name, const std::string &state):
122 ServerActiveObject(env, pos),
128 m_acceleration(0,0,0),
130 m_properties_sent(true),
132 m_last_sent_position(0,0,0),
133 m_last_sent_velocity(0,0,0),
134 m_last_sent_position_timer(0),
135 m_last_sent_move_precision(0),
136 m_armor_groups_sent(false),
137 m_animation_speed(0),
138 m_animation_blend(0),
139 m_animation_sent(false),
140 m_bone_position_sent(false),
141 m_attachment_parent_id(0),
142 m_attachment_sent(false)
144 // Only register type if no environment supplied
146 ServerActiveObject::registerType(getType(), create);
150 // Initialize something to armor groups
151 m_armor_groups["fleshy"] = 100;
154 LuaEntitySAO::~LuaEntitySAO()
157 m_env->getScriptIface()->luaentity_Remove(m_id);
161 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
163 ServerActiveObject::addedToEnvironment(dtime_s);
165 // Create entity from name
166 m_registered = m_env->getScriptIface()->
167 luaentity_Add(m_id, m_init_name.c_str());
171 m_env->getScriptIface()->
172 luaentity_GetProperties(m_id, &m_prop);
173 // Initialize HP from properties
174 m_hp = m_prop.hp_max;
175 // Activate entity, supplying serialized state
176 m_env->getScriptIface()->
177 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
181 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
182 const std::string &data)
190 std::istringstream is(data, std::ios::binary);
192 u8 version = readU8(is);
193 // check if version is supported
195 name = deSerializeString(is);
196 state = deSerializeLongString(is);
198 else if(version == 1){
199 name = deSerializeString(is);
200 state = deSerializeLongString(is);
202 velocity = readV3F1000(is);
207 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
208 <<state<<"\")"<<std::endl;
209 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
211 sao->m_velocity = velocity;
216 bool LuaEntitySAO::isAttached()
218 if(!m_attachment_parent_id)
220 // Check if the parent still exists
221 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
227 void LuaEntitySAO::step(float dtime, bool send_recommended)
229 if(!m_properties_sent)
231 m_properties_sent = true;
232 std::string str = getPropertyPacket();
233 // create message and add to list
234 ActiveObjectMessage aom(getId(), true, str);
235 m_messages_out.push(aom);
238 // If attached, check that our parent is still there. If it isn't, detach.
239 if(m_attachment_parent_id && !isAttached())
241 m_attachment_parent_id = 0;
242 m_attachment_bone = "";
243 m_attachment_position = v3f(0,0,0);
244 m_attachment_rotation = v3f(0,0,0);
245 sendPosition(false, true);
248 m_last_sent_position_timer += dtime;
250 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
251 // If the object gets detached this comes into effect automatically from the last known origin
254 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
255 m_base_position = pos;
256 m_velocity = v3f(0,0,0);
257 m_acceleration = v3f(0,0,0);
262 core::aabbox3d<f32> box = m_prop.collisionbox;
265 collisionMoveResult moveresult;
266 f32 pos_max_d = BS*0.25; // Distance per iteration
267 v3f p_pos = m_base_position;
268 v3f p_velocity = m_velocity;
269 v3f p_acceleration = m_acceleration;
270 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
271 pos_max_d, box, m_prop.stepheight, dtime,
272 p_pos, p_velocity, p_acceleration,
273 this, m_prop.collideWithObjects);
276 m_base_position = p_pos;
277 m_velocity = p_velocity;
278 m_acceleration = p_acceleration;
280 m_base_position += dtime * m_velocity + 0.5 * dtime
281 * dtime * m_acceleration;
282 m_velocity += dtime * m_acceleration;
285 if((m_prop.automatic_face_movement_dir) &&
286 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
287 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
292 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
295 if(send_recommended == false)
300 // TODO: force send when acceleration changes enough?
301 float minchange = 0.2*BS;
302 if(m_last_sent_position_timer > 1.0){
304 } else if(m_last_sent_position_timer > 0.2){
307 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
308 move_d += m_last_sent_move_precision;
309 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
310 if(move_d > minchange || vel_d > minchange ||
311 fabs(m_yaw - m_last_sent_yaw) > 1.0){
312 sendPosition(true, false);
316 if(m_armor_groups_sent == false){
317 m_armor_groups_sent = true;
318 std::string str = gob_cmd_update_armor_groups(
320 // create message and add to list
321 ActiveObjectMessage aom(getId(), true, str);
322 m_messages_out.push(aom);
325 if(m_animation_sent == false){
326 m_animation_sent = true;
327 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
328 // create message and add to list
329 ActiveObjectMessage aom(getId(), true, str);
330 m_messages_out.push(aom);
333 if(m_bone_position_sent == false){
334 m_bone_position_sent = true;
335 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
336 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
337 // create message and add to list
338 ActiveObjectMessage aom(getId(), true, str);
339 m_messages_out.push(aom);
343 if(m_attachment_sent == false){
344 m_attachment_sent = true;
345 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
346 // create message and add to list
347 ActiveObjectMessage aom(getId(), true, str);
348 m_messages_out.push(aom);
352 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
354 std::ostringstream os(std::ios::binary);
356 if(protocol_version >= 14)
358 writeU8(os, 1); // version
359 os<<serializeString(""); // name
360 writeU8(os, 0); // is_player
361 writeS16(os, getId()); //id
362 writeV3F1000(os, m_base_position);
363 writeF1000(os, m_yaw);
366 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
367 os<<serializeLongString(getPropertyPacket()); // message 1
368 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
369 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
370 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
371 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
373 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
377 writeU8(os, 0); // version
378 os<<serializeString(""); // name
379 writeU8(os, 0); // is_player
380 writeV3F1000(os, m_base_position);
381 writeF1000(os, m_yaw);
383 writeU8(os, 2); // number of messages stuffed in here
384 os<<serializeLongString(getPropertyPacket()); // message 1
385 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
392 std::string LuaEntitySAO::getStaticData()
394 verbosestream<<__FUNCTION_NAME<<std::endl;
395 std::ostringstream os(std::ios::binary);
399 os<<serializeString(m_init_name);
402 std::string state = m_env->getScriptIface()->
403 luaentity_GetStaticdata(m_id);
404 os<<serializeLongString(state);
406 os<<serializeLongString(m_init_state);
411 writeV3F1000(os, m_velocity);
413 writeF1000(os, m_yaw);
417 int LuaEntitySAO::punch(v3f dir,
418 const ToolCapabilities *toolcap,
419 ServerActiveObject *puncher,
420 float time_from_last_punch)
423 // Delete unknown LuaEntities when punched
428 // It's best that attachments cannot be punched
432 ItemStack *punchitem = NULL;
433 ItemStack punchitem_static;
435 punchitem_static = puncher->getWieldedItem();
436 punchitem = &punchitem_static;
439 PunchDamageResult result = getPunchDamage(
443 time_from_last_punch);
447 setHP(getHP() - result.damage);
450 std::string punchername = "nil";
453 punchername = puncher->getDescription();
455 actionstream<<getDescription()<<" punched by "
456 <<punchername<<", damage "<<result.damage
457 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
460 std::string str = gob_cmd_punched(result.damage, getHP());
461 // create message and add to list
462 ActiveObjectMessage aom(getId(), true, str);
463 m_messages_out.push(aom);
470 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
471 time_from_last_punch, toolcap, dir);
476 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
480 // It's best that attachments cannot be clicked
483 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
486 void LuaEntitySAO::setPos(v3f pos)
490 m_base_position = pos;
491 sendPosition(false, true);
494 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
498 m_base_position = pos;
500 sendPosition(true, true);
503 float LuaEntitySAO::getMinimumSavedMovement()
508 std::string LuaEntitySAO::getDescription()
510 std::ostringstream os(std::ios::binary);
511 os<<"LuaEntitySAO at (";
512 os<<(m_base_position.X/BS)<<",";
513 os<<(m_base_position.Y/BS)<<",";
514 os<<(m_base_position.Z/BS);
519 void LuaEntitySAO::setHP(s16 hp)
525 s16 LuaEntitySAO::getHP() const
530 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
532 m_armor_groups = armor_groups;
533 m_armor_groups_sent = false;
536 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
538 m_animation_range = frame_range;
539 m_animation_speed = frame_speed;
540 m_animation_blend = frame_blend;
541 m_animation_sent = false;
544 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
546 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
547 m_bone_position_sent = false;
550 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
552 // Attachments need to be handled on both the server and client.
553 // If we just attach on the server, we can only copy the position of the parent. Attachments
554 // are still sent to clients at an interval so players might see them lagging, plus we can't
555 // read and attach to skeletal bones.
556 // If we just attach on the client, the server still sees the child at its original location.
557 // This breaks some things so we also give the server the most accurate representation
558 // even if players only see the client changes.
560 m_attachment_parent_id = parent_id;
561 m_attachment_bone = bone;
562 m_attachment_position = position;
563 m_attachment_rotation = rotation;
564 m_attachment_sent = false;
567 ObjectProperties* LuaEntitySAO::accessObjectProperties()
572 void LuaEntitySAO::notifyObjectPropertiesModified()
574 m_properties_sent = false;
577 void LuaEntitySAO::setVelocity(v3f velocity)
579 m_velocity = velocity;
582 v3f LuaEntitySAO::getVelocity()
587 void LuaEntitySAO::setAcceleration(v3f acceleration)
589 m_acceleration = acceleration;
592 v3f LuaEntitySAO::getAcceleration()
594 return m_acceleration;
597 void LuaEntitySAO::setYaw(float yaw)
602 float LuaEntitySAO::getYaw()
607 void LuaEntitySAO::setTextureMod(const std::string &mod)
609 std::string str = gob_cmd_set_texture_mod(mod);
610 // create message and add to list
611 ActiveObjectMessage aom(getId(), true, str);
612 m_messages_out.push(aom);
615 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
616 bool select_horiz_by_yawpitch)
618 std::string str = gob_cmd_set_sprite(
622 select_horiz_by_yawpitch
624 // create message and add to list
625 ActiveObjectMessage aom(getId(), true, str);
626 m_messages_out.push(aom);
629 std::string LuaEntitySAO::getName()
634 std::string LuaEntitySAO::getPropertyPacket()
636 return gob_cmd_set_properties(m_prop);
639 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
641 // If the object is attached client-side, don't waste bandwidth sending its position to clients
645 m_last_sent_move_precision = m_base_position.getDistanceFrom(
646 m_last_sent_position);
647 m_last_sent_position_timer = 0;
648 m_last_sent_yaw = m_yaw;
649 m_last_sent_position = m_base_position;
650 m_last_sent_velocity = m_velocity;
651 //m_last_sent_acceleration = m_acceleration;
653 float update_interval = m_env->getSendRecommendedInterval();
655 std::string str = gob_cmd_update_position(
664 // create message and add to list
665 ActiveObjectMessage aom(getId(), false, str);
666 m_messages_out.push(aom);
669 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
672 //update collision box
673 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
674 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
676 toset->MinEdge += m_base_position;
677 toset->MaxEdge += m_base_position;
685 bool LuaEntitySAO::collideWithObjects(){
686 return m_prop.collideWithObjects;
693 // No prototype, PlayerSAO does not need to be deserialized
695 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
696 const std::set<std::string> &privs, bool is_singleplayer):
697 ServerActiveObject(env_, v3f(0,0,0)),
702 m_last_good_position(0,0,0),
703 m_time_from_last_punch(0),
704 m_nocheat_dig_pos(32767, 32767, 32767),
705 m_nocheat_dig_time(0),
707 m_position_not_sent(false),
708 m_armor_groups_sent(false),
709 m_properties_sent(true),
711 m_is_singleplayer(is_singleplayer),
712 m_animation_speed(0),
713 m_animation_blend(0),
714 m_animation_sent(false),
715 m_bone_position_sent(false),
716 m_attachment_parent_id(0),
717 m_attachment_sent(false),
718 m_nametag_color(video::SColor(255, 255, 255, 255)),
719 m_nametag_sent(false),
721 m_physics_override_speed(1),
722 m_physics_override_jump(1),
723 m_physics_override_gravity(1),
724 m_physics_override_sneak(true),
725 m_physics_override_sneak_glitch(true),
726 m_physics_override_sent(false)
728 assert(m_player); // pre-condition
729 assert(m_peer_id != 0); // pre-condition
730 setBasePosition(m_player->getPosition());
731 m_inventory = &m_player->inventory;
732 m_armor_groups["fleshy"] = 100;
734 m_prop.hp_max = PLAYER_MAX_HP;
735 m_prop.physical = false;
737 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
738 // start of default appearance, this should be overwritten by LUA
739 m_prop.visual = "upright_sprite";
740 m_prop.visual_size = v2f(1, 2);
741 m_prop.textures.clear();
742 m_prop.textures.push_back("player.png");
743 m_prop.textures.push_back("player_back.png");
744 m_prop.colors.clear();
745 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
746 m_prop.spritediv = v2s16(1,1);
747 // end of default appearance
748 m_prop.is_visible = true;
749 m_prop.makes_footstep_sound = true;
752 PlayerSAO::~PlayerSAO()
754 if(m_inventory != &m_player->inventory)
759 std::string PlayerSAO::getDescription()
761 return std::string("player ") + m_player->getName();
764 // Called after id has been set and has been inserted in environment
765 void PlayerSAO::addedToEnvironment(u32 dtime_s)
767 ServerActiveObject::addedToEnvironment(dtime_s);
768 ServerActiveObject::setBasePosition(m_player->getPosition());
769 m_player->setPlayerSAO(this);
770 m_player->peer_id = m_peer_id;
771 m_last_good_position = m_player->getPosition();
774 // Called before removing from environment
775 void PlayerSAO::removingFromEnvironment()
777 ServerActiveObject::removingFromEnvironment();
778 if(m_player->getPlayerSAO() == this)
780 m_player->setPlayerSAO(NULL);
781 m_player->peer_id = 0;
782 m_env->savePlayer(m_player->getName());
783 m_env->removePlayer(m_player->getName());
787 bool PlayerSAO::isStaticAllowed() const
792 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
794 std::ostringstream os(std::ios::binary);
796 if(protocol_version >= 15)
798 writeU8(os, 1); // version
799 os<<serializeString(m_player->getName()); // name
800 writeU8(os, 1); // is_player
801 writeS16(os, getId()); //id
802 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
803 writeF1000(os, m_player->getYaw());
804 writeS16(os, getHP());
806 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
807 os<<serializeLongString(getPropertyPacket()); // message 1
808 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
809 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
810 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
811 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
813 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
814 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
815 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
816 m_physics_override_sneak_glitch)); // 5
817 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
821 writeU8(os, 0); // version
822 os<<serializeString(m_player->getName()); // name
823 writeU8(os, 1); // is_player
824 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
825 writeF1000(os, m_player->getYaw());
826 writeS16(os, getHP());
827 writeU8(os, 2); // number of messages stuffed in here
828 os<<serializeLongString(getPropertyPacket()); // message 1
829 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
836 std::string PlayerSAO::getStaticData()
838 FATAL_ERROR("Deprecated function (?)");
842 bool PlayerSAO::isAttached()
844 if(!m_attachment_parent_id)
846 // Check if the parent still exists
847 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
853 void PlayerSAO::step(float dtime, bool send_recommended)
855 if(!m_properties_sent)
857 m_properties_sent = true;
858 std::string str = getPropertyPacket();
859 // create message and add to list
860 ActiveObjectMessage aom(getId(), true, str);
861 m_messages_out.push(aom);
864 // If attached, check that our parent is still there. If it isn't, detach.
865 if(m_attachment_parent_id && !isAttached())
867 m_attachment_parent_id = 0;
868 m_attachment_bone = "";
869 m_attachment_position = v3f(0,0,0);
870 m_attachment_rotation = v3f(0,0,0);
871 m_player->setPosition(m_last_good_position);
872 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
875 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
877 // Set lag pool maximums based on estimated lag
878 const float LAG_POOL_MIN = 5.0;
879 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
880 if(lag_pool_max < LAG_POOL_MIN)
881 lag_pool_max = LAG_POOL_MIN;
882 m_dig_pool.setMax(lag_pool_max);
883 m_move_pool.setMax(lag_pool_max);
885 // Increment cheat prevention timers
886 m_dig_pool.add(dtime);
887 m_move_pool.add(dtime);
888 m_time_from_last_punch += dtime;
889 m_nocheat_dig_time += dtime;
891 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
892 // If the object gets detached this comes into effect automatically from the last known origin
895 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
896 m_last_good_position = pos;
897 m_player->setPosition(pos);
900 if(send_recommended == false)
903 // If the object is attached client-side, don't waste bandwidth sending its position to clients
904 if(m_position_not_sent && !isAttached())
906 m_position_not_sent = false;
907 float update_interval = m_env->getSendRecommendedInterval();
909 if(isAttached()) // Just in case we ever do send attachment position too
910 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
912 pos = m_player->getPosition() + v3f(0,BS*1,0);
913 std::string str = gob_cmd_update_position(
922 // create message and add to list
923 ActiveObjectMessage aom(getId(), false, str);
924 m_messages_out.push(aom);
927 if(m_armor_groups_sent == false) {
928 m_armor_groups_sent = true;
929 std::string str = gob_cmd_update_armor_groups(
931 // create message and add to list
932 ActiveObjectMessage aom(getId(), true, str);
933 m_messages_out.push(aom);
936 if(m_physics_override_sent == false){
937 m_physics_override_sent = true;
938 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
939 m_physics_override_jump, m_physics_override_gravity,
940 m_physics_override_sneak, m_physics_override_sneak_glitch);
941 // create message and add to list
942 ActiveObjectMessage aom(getId(), true, str);
943 m_messages_out.push(aom);
946 if(m_animation_sent == false){
947 m_animation_sent = true;
948 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
949 // create message and add to list
950 ActiveObjectMessage aom(getId(), true, str);
951 m_messages_out.push(aom);
954 if(m_bone_position_sent == false){
955 m_bone_position_sent = true;
956 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
957 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
958 // create message and add to list
959 ActiveObjectMessage aom(getId(), true, str);
960 m_messages_out.push(aom);
964 if(m_attachment_sent == false){
965 m_attachment_sent = true;
966 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
967 // create message and add to list
968 ActiveObjectMessage aom(getId(), true, str);
969 m_messages_out.push(aom);
972 if (m_nametag_sent == false) {
973 m_nametag_sent = true;
974 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
975 // create message and add to list
976 ActiveObjectMessage aom(getId(), true, str);
977 m_messages_out.push(aom);
981 void PlayerSAO::setBasePosition(const v3f &position)
983 // This needs to be ran for attachments too
984 ServerActiveObject::setBasePosition(position);
985 m_position_not_sent = true;
988 void PlayerSAO::setPos(v3f pos)
992 m_player->setPosition(pos);
993 // Movement caused by this command is always valid
994 m_last_good_position = pos;
995 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
998 void PlayerSAO::moveTo(v3f pos, bool continuous)
1002 m_player->setPosition(pos);
1003 // Movement caused by this command is always valid
1004 m_last_good_position = pos;
1005 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1008 void PlayerSAO::setYaw(float yaw)
1010 m_player->setYaw(yaw);
1011 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1014 void PlayerSAO::setPitch(float pitch)
1016 m_player->setPitch(pitch);
1017 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1020 int PlayerSAO::punch(v3f dir,
1021 const ToolCapabilities *toolcap,
1022 ServerActiveObject *puncher,
1023 float time_from_last_punch)
1025 // It's best that attachments cannot be punched
1032 // No effect if PvP disabled
1033 if (g_settings->getBool("enable_pvp") == false) {
1034 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1035 std::string str = gob_cmd_punched(0, getHP());
1036 // create message and add to list
1037 ActiveObjectMessage aom(getId(), true, str);
1038 m_messages_out.push(aom);
1043 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1044 time_from_last_punch);
1046 std::string punchername = "nil";
1049 punchername = puncher->getDescription();
1051 PlayerSAO *playersao = m_player->getPlayerSAO();
1053 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1054 puncher, time_from_last_punch, toolcap, dir,
1057 if (!damage_handled) {
1058 setHP(getHP() - hitparams.hp);
1059 } else { // override client prediction
1060 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1061 std::string str = gob_cmd_punched(0, getHP());
1062 // create message and add to list
1063 ActiveObjectMessage aom(getId(), true, str);
1064 m_messages_out.push(aom);
1069 actionstream << "Player " << m_player->getName() << " punched by "
1071 if (!damage_handled) {
1072 actionstream << ", damage " << hitparams.hp << " HP";
1074 actionstream << ", damage handled by lua";
1076 actionstream << std::endl;
1078 return hitparams.wear;
1081 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1085 s16 PlayerSAO::getHP() const
1087 return m_player->hp;
1090 s16 PlayerSAO::readDamage()
1092 s16 damage = m_damage;
1097 void PlayerSAO::setHP(s16 hp)
1099 s16 oldhp = m_player->hp;
1103 else if (hp > PLAYER_MAX_HP)
1106 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1113 m_damage += (oldhp - hp);
1115 // Update properties on death
1116 if ((hp == 0) != (oldhp == 0))
1117 m_properties_sent = false;
1120 u16 PlayerSAO::getBreath() const
1122 return m_player->getBreath();
1125 void PlayerSAO::setBreath(u16 breath)
1127 m_player->setBreath(breath);
1130 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1132 m_armor_groups = armor_groups;
1133 m_armor_groups_sent = false;
1136 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1138 // store these so they can be updated to clients
1139 m_animation_range = frame_range;
1140 m_animation_speed = frame_speed;
1141 m_animation_blend = frame_blend;
1142 m_animation_sent = false;
1145 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1147 // store these so they can be updated to clients
1148 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1149 m_bone_position_sent = false;
1152 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1154 // Attachments need to be handled on both the server and client.
1155 // If we just attach on the server, we can only copy the position of the parent. Attachments
1156 // are still sent to clients at an interval so players might see them lagging, plus we can't
1157 // read and attach to skeletal bones.
1158 // If we just attach on the client, the server still sees the child at its original location.
1159 // This breaks some things so we also give the server the most accurate representation
1160 // even if players only see the client changes.
1162 m_attachment_parent_id = parent_id;
1163 m_attachment_bone = bone;
1164 m_attachment_position = position;
1165 m_attachment_rotation = rotation;
1166 m_attachment_sent = false;
1169 ObjectProperties* PlayerSAO::accessObjectProperties()
1174 void PlayerSAO::notifyObjectPropertiesModified()
1176 m_properties_sent = false;
1179 void PlayerSAO::setNametagColor(video::SColor color)
1181 m_nametag_color = color;
1182 m_nametag_sent = false;
1185 video::SColor PlayerSAO::getNametagColor()
1187 return m_nametag_color;
1190 Inventory* PlayerSAO::getInventory()
1194 const Inventory* PlayerSAO::getInventory() const
1199 InventoryLocation PlayerSAO::getInventoryLocation() const
1201 InventoryLocation loc;
1202 loc.setPlayer(m_player->getName());
1206 std::string PlayerSAO::getWieldList() const
1211 int PlayerSAO::getWieldIndex() const
1213 return m_wield_index;
1216 void PlayerSAO::setWieldIndex(int i)
1218 if(i != m_wield_index) {
1223 void PlayerSAO::disconnected()
1227 if(m_player->getPlayerSAO() == this)
1229 m_player->setPlayerSAO(NULL);
1230 m_player->peer_id = 0;
1234 std::string PlayerSAO::getPropertyPacket()
1236 m_prop.is_visible = (true);
1237 return gob_cmd_set_properties(m_prop);
1240 bool PlayerSAO::checkMovementCheat()
1242 bool cheated = false;
1243 if(isAttached() || m_is_singleplayer ||
1244 g_settings->getBool("disable_anticheat"))
1246 m_last_good_position = m_player->getPosition();
1251 Check player movements
1253 NOTE: Actually the server should handle player physics like the
1254 client does and compare player's position to what is calculated
1255 on our side. This is required when eg. players fly due to an
1256 explosion. Altough a node-based alternative might be possible
1257 too, and much more lightweight.
1260 float player_max_speed = 0;
1261 if(m_privs.count("fast") != 0){
1263 player_max_speed = m_player->movement_speed_fast;
1266 player_max_speed = m_player->movement_speed_walk;
1268 // Tolerance. With the lag pool we shouldn't need it.
1269 //player_max_speed *= 2.5;
1270 //player_max_speed_up *= 2.5;
1272 v3f diff = (m_player->getPosition() - m_last_good_position);
1273 float d_vert = diff.Y;
1275 float d_horiz = diff.getLength();
1276 float required_time = d_horiz/player_max_speed;
1277 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1278 required_time = d_vert/player_max_speed;
1279 if(m_move_pool.grab(required_time)){
1280 m_last_good_position = m_player->getPosition();
1282 actionstream<<"Player "<<m_player->getName()
1283 <<" moved too fast; resetting position"
1285 m_player->setPosition(m_last_good_position);
1292 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1293 //update collision box
1294 *toset = m_player->getCollisionbox();
1296 toset->MinEdge += m_base_position;
1297 toset->MaxEdge += m_base_position;
1302 bool PlayerSAO::collideWithObjects(){