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 ItemGroupList LuaEntitySAO::getArmorGroups()
538 return m_armor_groups;
541 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
543 m_animation_range = frame_range;
544 m_animation_speed = frame_speed;
545 m_animation_blend = frame_blend;
546 m_animation_sent = false;
549 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend)
551 *frame_range = m_animation_range;
552 *frame_speed = m_animation_speed;
553 *frame_blend = m_animation_blend;
556 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
558 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
559 m_bone_position_sent = false;
562 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
564 *position = m_bone_position[bone].X;
565 *rotation = m_bone_position[bone].Y;
568 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
570 // Attachments need to be handled on both the server and client.
571 // If we just attach on the server, we can only copy the position of the parent. Attachments
572 // are still sent to clients at an interval so players might see them lagging, plus we can't
573 // read and attach to skeletal bones.
574 // If we just attach on the client, the server still sees the child at its original location.
575 // This breaks some things so we also give the server the most accurate representation
576 // even if players only see the client changes.
578 m_attachment_parent_id = parent_id;
579 m_attachment_bone = bone;
580 m_attachment_position = position;
581 m_attachment_rotation = rotation;
582 m_attachment_sent = false;
585 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
588 *parent_id = m_attachment_parent_id;
589 *bone = m_attachment_bone;
590 *position = m_attachment_position;
591 *rotation = m_attachment_rotation;
594 ObjectProperties* LuaEntitySAO::accessObjectProperties()
599 void LuaEntitySAO::notifyObjectPropertiesModified()
601 m_properties_sent = false;
604 void LuaEntitySAO::setVelocity(v3f velocity)
606 m_velocity = velocity;
609 v3f LuaEntitySAO::getVelocity()
614 void LuaEntitySAO::setAcceleration(v3f acceleration)
616 m_acceleration = acceleration;
619 v3f LuaEntitySAO::getAcceleration()
621 return m_acceleration;
624 void LuaEntitySAO::setYaw(float yaw)
629 float LuaEntitySAO::getYaw()
634 void LuaEntitySAO::setTextureMod(const std::string &mod)
636 std::string str = gob_cmd_set_texture_mod(mod);
637 // create message and add to list
638 ActiveObjectMessage aom(getId(), true, str);
639 m_messages_out.push(aom);
642 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
643 bool select_horiz_by_yawpitch)
645 std::string str = gob_cmd_set_sprite(
649 select_horiz_by_yawpitch
651 // create message and add to list
652 ActiveObjectMessage aom(getId(), true, str);
653 m_messages_out.push(aom);
656 std::string LuaEntitySAO::getName()
661 std::string LuaEntitySAO::getPropertyPacket()
663 return gob_cmd_set_properties(m_prop);
666 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
668 // If the object is attached client-side, don't waste bandwidth sending its position to clients
672 m_last_sent_move_precision = m_base_position.getDistanceFrom(
673 m_last_sent_position);
674 m_last_sent_position_timer = 0;
675 m_last_sent_yaw = m_yaw;
676 m_last_sent_position = m_base_position;
677 m_last_sent_velocity = m_velocity;
678 //m_last_sent_acceleration = m_acceleration;
680 float update_interval = m_env->getSendRecommendedInterval();
682 std::string str = gob_cmd_update_position(
691 // create message and add to list
692 ActiveObjectMessage aom(getId(), false, str);
693 m_messages_out.push(aom);
696 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
699 //update collision box
700 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
701 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
703 toset->MinEdge += m_base_position;
704 toset->MaxEdge += m_base_position;
712 bool LuaEntitySAO::collideWithObjects(){
713 return m_prop.collideWithObjects;
720 // No prototype, PlayerSAO does not need to be deserialized
722 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
723 const std::set<std::string> &privs, bool is_singleplayer):
724 ServerActiveObject(env_, v3f(0,0,0)),
729 m_last_good_position(0,0,0),
730 m_time_from_last_punch(0),
731 m_nocheat_dig_pos(32767, 32767, 32767),
732 m_nocheat_dig_time(0),
734 m_position_not_sent(false),
735 m_armor_groups_sent(false),
736 m_properties_sent(true),
738 m_is_singleplayer(is_singleplayer),
739 m_animation_speed(0),
740 m_animation_blend(0),
741 m_animation_sent(false),
742 m_bone_position_sent(false),
743 m_attachment_parent_id(0),
744 m_attachment_sent(false),
745 m_nametag_color(video::SColor(255, 255, 255, 255)),
746 m_nametag_sent(false),
748 m_physics_override_speed(1),
749 m_physics_override_jump(1),
750 m_physics_override_gravity(1),
751 m_physics_override_sneak(true),
752 m_physics_override_sneak_glitch(true),
753 m_physics_override_sent(false)
755 assert(m_player); // pre-condition
756 assert(m_peer_id != 0); // pre-condition
757 setBasePosition(m_player->getPosition());
758 m_inventory = &m_player->inventory;
759 m_armor_groups["fleshy"] = 100;
761 m_prop.hp_max = PLAYER_MAX_HP;
762 m_prop.physical = false;
764 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
765 // start of default appearance, this should be overwritten by LUA
766 m_prop.visual = "upright_sprite";
767 m_prop.visual_size = v2f(1, 2);
768 m_prop.textures.clear();
769 m_prop.textures.push_back("player.png");
770 m_prop.textures.push_back("player_back.png");
771 m_prop.colors.clear();
772 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
773 m_prop.spritediv = v2s16(1,1);
774 // end of default appearance
775 m_prop.is_visible = true;
776 m_prop.makes_footstep_sound = true;
779 PlayerSAO::~PlayerSAO()
781 if(m_inventory != &m_player->inventory)
786 std::string PlayerSAO::getDescription()
788 return std::string("player ") + m_player->getName();
791 // Called after id has been set and has been inserted in environment
792 void PlayerSAO::addedToEnvironment(u32 dtime_s)
794 ServerActiveObject::addedToEnvironment(dtime_s);
795 ServerActiveObject::setBasePosition(m_player->getPosition());
796 m_player->setPlayerSAO(this);
797 m_player->peer_id = m_peer_id;
798 m_last_good_position = m_player->getPosition();
801 // Called before removing from environment
802 void PlayerSAO::removingFromEnvironment()
804 ServerActiveObject::removingFromEnvironment();
805 if(m_player->getPlayerSAO() == this)
807 m_player->setPlayerSAO(NULL);
808 m_player->peer_id = 0;
809 m_env->savePlayer(m_player->getName());
810 m_env->removePlayer(m_player->getName());
814 bool PlayerSAO::isStaticAllowed() const
819 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
821 std::ostringstream os(std::ios::binary);
823 if(protocol_version >= 15)
825 writeU8(os, 1); // version
826 os<<serializeString(m_player->getName()); // name
827 writeU8(os, 1); // is_player
828 writeS16(os, getId()); //id
829 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
830 writeF1000(os, m_player->getYaw());
831 writeS16(os, getHP());
833 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
834 os<<serializeLongString(getPropertyPacket()); // message 1
835 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
836 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
837 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
838 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
840 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
841 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
842 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
843 m_physics_override_sneak_glitch)); // 5
844 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
848 writeU8(os, 0); // version
849 os<<serializeString(m_player->getName()); // name
850 writeU8(os, 1); // is_player
851 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
852 writeF1000(os, m_player->getYaw());
853 writeS16(os, getHP());
854 writeU8(os, 2); // number of messages stuffed in here
855 os<<serializeLongString(getPropertyPacket()); // message 1
856 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
863 std::string PlayerSAO::getStaticData()
865 FATAL_ERROR("Deprecated function (?)");
869 bool PlayerSAO::isAttached()
871 if(!m_attachment_parent_id)
873 // Check if the parent still exists
874 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
880 void PlayerSAO::step(float dtime, bool send_recommended)
882 if(!m_properties_sent)
884 m_properties_sent = true;
885 std::string str = getPropertyPacket();
886 // create message and add to list
887 ActiveObjectMessage aom(getId(), true, str);
888 m_messages_out.push(aom);
891 // If attached, check that our parent is still there. If it isn't, detach.
892 if(m_attachment_parent_id && !isAttached())
894 m_attachment_parent_id = 0;
895 m_attachment_bone = "";
896 m_attachment_position = v3f(0,0,0);
897 m_attachment_rotation = v3f(0,0,0);
898 m_player->setPosition(m_last_good_position);
899 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
902 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
904 // Set lag pool maximums based on estimated lag
905 const float LAG_POOL_MIN = 5.0;
906 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
907 if(lag_pool_max < LAG_POOL_MIN)
908 lag_pool_max = LAG_POOL_MIN;
909 m_dig_pool.setMax(lag_pool_max);
910 m_move_pool.setMax(lag_pool_max);
912 // Increment cheat prevention timers
913 m_dig_pool.add(dtime);
914 m_move_pool.add(dtime);
915 m_time_from_last_punch += dtime;
916 m_nocheat_dig_time += dtime;
918 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
919 // If the object gets detached this comes into effect automatically from the last known origin
922 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
923 m_last_good_position = pos;
924 m_player->setPosition(pos);
927 if(send_recommended == false)
930 // If the object is attached client-side, don't waste bandwidth sending its position to clients
931 if(m_position_not_sent && !isAttached())
933 m_position_not_sent = false;
934 float update_interval = m_env->getSendRecommendedInterval();
936 if(isAttached()) // Just in case we ever do send attachment position too
937 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
939 pos = m_player->getPosition() + v3f(0,BS*1,0);
940 std::string str = gob_cmd_update_position(
949 // create message and add to list
950 ActiveObjectMessage aom(getId(), false, str);
951 m_messages_out.push(aom);
954 if(m_armor_groups_sent == false) {
955 m_armor_groups_sent = true;
956 std::string str = gob_cmd_update_armor_groups(
958 // create message and add to list
959 ActiveObjectMessage aom(getId(), true, str);
960 m_messages_out.push(aom);
963 if(m_physics_override_sent == false){
964 m_physics_override_sent = true;
965 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
966 m_physics_override_jump, m_physics_override_gravity,
967 m_physics_override_sneak, m_physics_override_sneak_glitch);
968 // create message and add to list
969 ActiveObjectMessage aom(getId(), true, str);
970 m_messages_out.push(aom);
973 if(m_animation_sent == false){
974 m_animation_sent = true;
975 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
976 // create message and add to list
977 ActiveObjectMessage aom(getId(), true, str);
978 m_messages_out.push(aom);
981 if(m_bone_position_sent == false){
982 m_bone_position_sent = true;
983 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
984 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
985 // create message and add to list
986 ActiveObjectMessage aom(getId(), true, str);
987 m_messages_out.push(aom);
991 if(m_attachment_sent == false){
992 m_attachment_sent = true;
993 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
994 // create message and add to list
995 ActiveObjectMessage aom(getId(), true, str);
996 m_messages_out.push(aom);
999 if (m_nametag_sent == false) {
1000 m_nametag_sent = true;
1001 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
1002 // create message and add to list
1003 ActiveObjectMessage aom(getId(), true, str);
1004 m_messages_out.push(aom);
1008 void PlayerSAO::setBasePosition(const v3f &position)
1010 // This needs to be ran for attachments too
1011 ServerActiveObject::setBasePosition(position);
1012 m_position_not_sent = true;
1015 void PlayerSAO::setPos(v3f pos)
1019 m_player->setPosition(pos);
1020 // Movement caused by this command is always valid
1021 m_last_good_position = pos;
1022 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1025 void PlayerSAO::moveTo(v3f pos, bool continuous)
1029 m_player->setPosition(pos);
1030 // Movement caused by this command is always valid
1031 m_last_good_position = pos;
1032 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1035 void PlayerSAO::setYaw(float yaw)
1037 m_player->setYaw(yaw);
1038 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1041 void PlayerSAO::setPitch(float pitch)
1043 m_player->setPitch(pitch);
1044 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1047 int PlayerSAO::punch(v3f dir,
1048 const ToolCapabilities *toolcap,
1049 ServerActiveObject *puncher,
1050 float time_from_last_punch)
1052 // It's best that attachments cannot be punched
1059 // No effect if PvP disabled
1060 if (g_settings->getBool("enable_pvp") == false) {
1061 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1062 std::string str = gob_cmd_punched(0, getHP());
1063 // create message and add to list
1064 ActiveObjectMessage aom(getId(), true, str);
1065 m_messages_out.push(aom);
1070 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1071 time_from_last_punch);
1073 std::string punchername = "nil";
1076 punchername = puncher->getDescription();
1078 PlayerSAO *playersao = m_player->getPlayerSAO();
1080 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1081 puncher, time_from_last_punch, toolcap, dir,
1084 if (!damage_handled) {
1085 setHP(getHP() - hitparams.hp);
1086 } else { // override client prediction
1087 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1088 std::string str = gob_cmd_punched(0, getHP());
1089 // create message and add to list
1090 ActiveObjectMessage aom(getId(), true, str);
1091 m_messages_out.push(aom);
1096 actionstream << "Player " << m_player->getName() << " punched by "
1098 if (!damage_handled) {
1099 actionstream << ", damage " << hitparams.hp << " HP";
1101 actionstream << ", damage handled by lua";
1103 actionstream << std::endl;
1105 return hitparams.wear;
1108 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1112 s16 PlayerSAO::getHP() const
1114 return m_player->hp;
1117 s16 PlayerSAO::readDamage()
1119 s16 damage = m_damage;
1124 void PlayerSAO::setHP(s16 hp)
1126 s16 oldhp = m_player->hp;
1128 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1132 hp = oldhp + hp_change;
1136 else if (hp > PLAYER_MAX_HP)
1139 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1146 m_damage += (oldhp - hp);
1148 // Update properties on death
1149 if ((hp == 0) != (oldhp == 0))
1150 m_properties_sent = false;
1153 u16 PlayerSAO::getBreath() const
1155 return m_player->getBreath();
1158 void PlayerSAO::setBreath(u16 breath)
1160 m_player->setBreath(breath);
1163 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1165 m_armor_groups = armor_groups;
1166 m_armor_groups_sent = false;
1169 ItemGroupList PlayerSAO::getArmorGroups()
1171 return m_armor_groups;
1174 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1176 // store these so they can be updated to clients
1177 m_animation_range = frame_range;
1178 m_animation_speed = frame_speed;
1179 m_animation_blend = frame_blend;
1180 m_animation_sent = false;
1183 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend)
1185 *frame_range = m_animation_range;
1186 *frame_speed = m_animation_speed;
1187 *frame_blend = m_animation_blend;
1190 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1192 // store these so they can be updated to clients
1193 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1194 m_bone_position_sent = false;
1197 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1199 *position = m_bone_position[bone].X;
1200 *rotation = m_bone_position[bone].Y;
1203 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1205 // Attachments need to be handled on both the server and client.
1206 // If we just attach on the server, we can only copy the position of the parent. Attachments
1207 // are still sent to clients at an interval so players might see them lagging, plus we can't
1208 // read and attach to skeletal bones.
1209 // If we just attach on the client, the server still sees the child at its original location.
1210 // This breaks some things so we also give the server the most accurate representation
1211 // even if players only see the client changes.
1213 m_attachment_parent_id = parent_id;
1214 m_attachment_bone = bone;
1215 m_attachment_position = position;
1216 m_attachment_rotation = rotation;
1217 m_attachment_sent = false;
1220 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1223 *parent_id = m_attachment_parent_id;
1224 *bone = m_attachment_bone;
1225 *position = m_attachment_position;
1226 *rotation = m_attachment_rotation;
1229 ObjectProperties* PlayerSAO::accessObjectProperties()
1234 void PlayerSAO::notifyObjectPropertiesModified()
1236 m_properties_sent = false;
1239 void PlayerSAO::setNametagColor(video::SColor color)
1241 m_nametag_color = color;
1242 m_nametag_sent = false;
1245 video::SColor PlayerSAO::getNametagColor()
1247 return m_nametag_color;
1250 Inventory* PlayerSAO::getInventory()
1254 const Inventory* PlayerSAO::getInventory() const
1259 InventoryLocation PlayerSAO::getInventoryLocation() const
1261 InventoryLocation loc;
1262 loc.setPlayer(m_player->getName());
1266 std::string PlayerSAO::getWieldList() const
1271 int PlayerSAO::getWieldIndex() const
1273 return m_wield_index;
1276 void PlayerSAO::setWieldIndex(int i)
1278 if(i != m_wield_index) {
1283 void PlayerSAO::disconnected()
1287 if(m_player->getPlayerSAO() == this)
1289 m_player->setPlayerSAO(NULL);
1290 m_player->peer_id = 0;
1294 std::string PlayerSAO::getPropertyPacket()
1296 m_prop.is_visible = (true);
1297 return gob_cmd_set_properties(m_prop);
1300 bool PlayerSAO::checkMovementCheat()
1302 bool cheated = false;
1303 if(isAttached() || m_is_singleplayer ||
1304 g_settings->getBool("disable_anticheat"))
1306 m_last_good_position = m_player->getPosition();
1311 Check player movements
1313 NOTE: Actually the server should handle player physics like the
1314 client does and compare player's position to what is calculated
1315 on our side. This is required when eg. players fly due to an
1316 explosion. Altough a node-based alternative might be possible
1317 too, and much more lightweight.
1320 float player_max_speed = 0;
1321 if(m_privs.count("fast") != 0){
1323 player_max_speed = m_player->movement_speed_fast;
1326 player_max_speed = m_player->movement_speed_walk;
1328 // Tolerance. With the lag pool we shouldn't need it.
1329 //player_max_speed *= 2.5;
1330 //player_max_speed_up *= 2.5;
1332 v3f diff = (m_player->getPosition() - m_last_good_position);
1333 float d_vert = diff.Y;
1335 float d_horiz = diff.getLength();
1336 float required_time = d_horiz/player_max_speed;
1337 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1338 required_time = d_vert/player_max_speed;
1339 if(m_move_pool.grab(required_time)){
1340 m_last_good_position = m_player->getPosition();
1342 actionstream<<"Player "<<m_player->getName()
1343 <<" moved too fast; resetting position"
1345 m_player->setPosition(m_last_good_position);
1352 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1353 //update collision box
1354 *toset = m_player->getCollisionbox();
1356 toset->MinEdge += m_base_position;
1357 toset->MaxEdge += m_base_position;
1362 bool PlayerSAO::collideWithObjects(){