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"
26 #include "main.h" // For g_profiler
28 #include "serialization.h" // For compressZlib
29 #include "tool.h" // For ToolCapabilities
33 #include "scripting_game.h"
34 #include "genericobject.h"
37 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
43 class TestSAO : public ServerActiveObject
46 TestSAO(ServerEnvironment *env, v3f pos):
47 ServerActiveObject(env, pos),
51 ServerActiveObject::registerType(getType(), create);
53 ActiveObjectType getType() const
54 { return ACTIVEOBJECT_TYPE_TEST; }
56 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
57 const std::string &data)
59 return new TestSAO(env, pos);
62 void step(float dtime, bool send_recommended)
71 m_base_position.Y += dtime * BS * 2;
72 if(m_base_position.Y > 8*BS)
73 m_base_position.Y = 2*BS;
75 if(send_recommended == false)
85 data += itos(0); // 0 = position
87 data += itos(m_base_position.X);
89 data += itos(m_base_position.Y);
91 data += itos(m_base_position.Z);
93 ActiveObjectMessage aom(getId(), false, data);
94 m_messages_out.push(aom);
98 bool getCollisionBox(aabb3f *toset) {
102 bool collideWithObjects() {
111 // Prototype (registers item for deserialization)
112 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
118 // Prototype (registers item for deserialization)
119 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
121 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
122 const std::string &name, const std::string &state):
123 ServerActiveObject(env, pos),
129 m_acceleration(0,0,0),
131 m_properties_sent(true),
133 m_last_sent_position(0,0,0),
134 m_last_sent_velocity(0,0,0),
135 m_last_sent_position_timer(0),
136 m_last_sent_move_precision(0),
137 m_armor_groups_sent(false),
138 m_animation_speed(0),
139 m_animation_blend(0),
140 m_animation_sent(false),
141 m_bone_position_sent(false),
142 m_attachment_parent_id(0),
143 m_attachment_sent(false)
145 // Only register type if no environment supplied
147 ServerActiveObject::registerType(getType(), create);
151 // Initialize something to armor groups
152 m_armor_groups["fleshy"] = 100;
155 LuaEntitySAO::~LuaEntitySAO()
158 m_env->getScriptIface()->luaentity_Remove(m_id);
162 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
164 ServerActiveObject::addedToEnvironment(dtime_s);
166 // Create entity from name
167 m_registered = m_env->getScriptIface()->
168 luaentity_Add(m_id, m_init_name.c_str());
172 m_env->getScriptIface()->
173 luaentity_GetProperties(m_id, &m_prop);
174 // Initialize HP from properties
175 m_hp = m_prop.hp_max;
176 // Activate entity, supplying serialized state
177 m_env->getScriptIface()->
178 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
182 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
183 const std::string &data)
191 std::istringstream is(data, std::ios::binary);
193 u8 version = readU8(is);
194 // check if version is supported
196 name = deSerializeString(is);
197 state = deSerializeLongString(is);
199 else if(version == 1){
200 name = deSerializeString(is);
201 state = deSerializeLongString(is);
203 velocity = readV3F1000(is);
208 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
209 <<state<<"\")"<<std::endl;
210 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
212 sao->m_velocity = velocity;
217 bool LuaEntitySAO::isAttached()
219 if(!m_attachment_parent_id)
221 // Check if the parent still exists
222 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
228 void LuaEntitySAO::step(float dtime, bool send_recommended)
230 if(!m_properties_sent)
232 m_properties_sent = true;
233 std::string str = getPropertyPacket();
234 // create message and add to list
235 ActiveObjectMessage aom(getId(), true, str);
236 m_messages_out.push(aom);
239 // If attached, check that our parent is still there. If it isn't, detach.
240 if(m_attachment_parent_id && !isAttached())
242 m_attachment_parent_id = 0;
243 m_attachment_bone = "";
244 m_attachment_position = v3f(0,0,0);
245 m_attachment_rotation = v3f(0,0,0);
246 sendPosition(false, true);
249 m_last_sent_position_timer += dtime;
251 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
252 // If the object gets detached this comes into effect automatically from the last known origin
255 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
256 m_base_position = pos;
257 m_velocity = v3f(0,0,0);
258 m_acceleration = v3f(0,0,0);
263 core::aabbox3d<f32> box = m_prop.collisionbox;
266 collisionMoveResult moveresult;
267 f32 pos_max_d = BS*0.25; // Distance per iteration
268 v3f p_pos = m_base_position;
269 v3f p_velocity = m_velocity;
270 v3f p_acceleration = m_acceleration;
271 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
272 pos_max_d, box, m_prop.stepheight, dtime,
273 p_pos, p_velocity, p_acceleration,
274 this, m_prop.collideWithObjects);
277 m_base_position = p_pos;
278 m_velocity = p_velocity;
279 m_acceleration = p_acceleration;
281 m_base_position += dtime * m_velocity + 0.5 * dtime
282 * dtime * m_acceleration;
283 m_velocity += dtime * m_acceleration;
286 if((m_prop.automatic_face_movement_dir) &&
287 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
288 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
293 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
296 if(send_recommended == false)
301 // TODO: force send when acceleration changes enough?
302 float minchange = 0.2*BS;
303 if(m_last_sent_position_timer > 1.0){
305 } else if(m_last_sent_position_timer > 0.2){
308 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
309 move_d += m_last_sent_move_precision;
310 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
311 if(move_d > minchange || vel_d > minchange ||
312 fabs(m_yaw - m_last_sent_yaw) > 1.0){
313 sendPosition(true, false);
317 if(m_armor_groups_sent == false){
318 m_armor_groups_sent = true;
319 std::string str = gob_cmd_update_armor_groups(
321 // create message and add to list
322 ActiveObjectMessage aom(getId(), true, str);
323 m_messages_out.push(aom);
326 if(m_animation_sent == false){
327 m_animation_sent = true;
328 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
329 // create message and add to list
330 ActiveObjectMessage aom(getId(), true, str);
331 m_messages_out.push(aom);
334 if(m_bone_position_sent == false){
335 m_bone_position_sent = true;
336 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
337 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
338 // create message and add to list
339 ActiveObjectMessage aom(getId(), true, str);
340 m_messages_out.push(aom);
344 if(m_attachment_sent == false){
345 m_attachment_sent = true;
346 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
347 // create message and add to list
348 ActiveObjectMessage aom(getId(), true, str);
349 m_messages_out.push(aom);
353 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
355 std::ostringstream os(std::ios::binary);
357 if(protocol_version >= 14)
359 writeU8(os, 1); // version
360 os<<serializeString(""); // name
361 writeU8(os, 0); // is_player
362 writeS16(os, getId()); //id
363 writeV3F1000(os, m_base_position);
364 writeF1000(os, m_yaw);
367 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
368 os<<serializeLongString(getPropertyPacket()); // message 1
369 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
370 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
371 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
372 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
374 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
378 writeU8(os, 0); // version
379 os<<serializeString(""); // name
380 writeU8(os, 0); // is_player
381 writeV3F1000(os, m_base_position);
382 writeF1000(os, m_yaw);
384 writeU8(os, 2); // number of messages stuffed in here
385 os<<serializeLongString(getPropertyPacket()); // message 1
386 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
393 std::string LuaEntitySAO::getStaticData()
395 verbosestream<<__FUNCTION_NAME<<std::endl;
396 std::ostringstream os(std::ios::binary);
400 os<<serializeString(m_init_name);
403 std::string state = m_env->getScriptIface()->
404 luaentity_GetStaticdata(m_id);
405 os<<serializeLongString(state);
407 os<<serializeLongString(m_init_state);
412 writeV3F1000(os, m_velocity);
414 writeF1000(os, m_yaw);
418 int LuaEntitySAO::punch(v3f dir,
419 const ToolCapabilities *toolcap,
420 ServerActiveObject *puncher,
421 float time_from_last_punch)
424 // Delete unknown LuaEntities when punched
429 // It's best that attachments cannot be punched
433 ItemStack *punchitem = NULL;
434 ItemStack punchitem_static;
436 punchitem_static = puncher->getWieldedItem();
437 punchitem = &punchitem_static;
440 PunchDamageResult result = getPunchDamage(
444 time_from_last_punch);
448 setHP(getHP() - result.damage);
451 std::string punchername = "nil";
454 punchername = puncher->getDescription();
456 actionstream<<getDescription()<<" punched by "
457 <<punchername<<", damage "<<result.damage
458 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
461 std::string str = gob_cmd_punched(result.damage, getHP());
462 // create message and add to list
463 ActiveObjectMessage aom(getId(), true, str);
464 m_messages_out.push(aom);
471 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
472 time_from_last_punch, toolcap, dir);
477 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
481 // It's best that attachments cannot be clicked
484 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
487 void LuaEntitySAO::setPos(v3f pos)
491 m_base_position = pos;
492 sendPosition(false, true);
495 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
499 m_base_position = pos;
501 sendPosition(true, true);
504 float LuaEntitySAO::getMinimumSavedMovement()
509 std::string LuaEntitySAO::getDescription()
511 std::ostringstream os(std::ios::binary);
512 os<<"LuaEntitySAO at (";
513 os<<(m_base_position.X/BS)<<",";
514 os<<(m_base_position.Y/BS)<<",";
515 os<<(m_base_position.Z/BS);
520 void LuaEntitySAO::setHP(s16 hp)
526 s16 LuaEntitySAO::getHP() const
531 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
533 m_armor_groups = armor_groups;
534 m_armor_groups_sent = false;
537 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
539 m_animation_range = frame_range;
540 m_animation_speed = frame_speed;
541 m_animation_blend = frame_blend;
542 m_animation_sent = false;
545 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
547 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
548 m_bone_position_sent = false;
551 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
553 // Attachments need to be handled on both the server and client.
554 // If we just attach on the server, we can only copy the position of the parent. Attachments
555 // are still sent to clients at an interval so players might see them lagging, plus we can't
556 // read and attach to skeletal bones.
557 // If we just attach on the client, the server still sees the child at its original location.
558 // This breaks some things so we also give the server the most accurate representation
559 // even if players only see the client changes.
561 m_attachment_parent_id = parent_id;
562 m_attachment_bone = bone;
563 m_attachment_position = position;
564 m_attachment_rotation = rotation;
565 m_attachment_sent = false;
568 ObjectProperties* LuaEntitySAO::accessObjectProperties()
573 void LuaEntitySAO::notifyObjectPropertiesModified()
575 m_properties_sent = false;
578 void LuaEntitySAO::setVelocity(v3f velocity)
580 m_velocity = velocity;
583 v3f LuaEntitySAO::getVelocity()
588 void LuaEntitySAO::setAcceleration(v3f acceleration)
590 m_acceleration = acceleration;
593 v3f LuaEntitySAO::getAcceleration()
595 return m_acceleration;
598 void LuaEntitySAO::setYaw(float yaw)
603 float LuaEntitySAO::getYaw()
608 void LuaEntitySAO::setTextureMod(const std::string &mod)
610 std::string str = gob_cmd_set_texture_mod(mod);
611 // create message and add to list
612 ActiveObjectMessage aom(getId(), true, str);
613 m_messages_out.push(aom);
616 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
617 bool select_horiz_by_yawpitch)
619 std::string str = gob_cmd_set_sprite(
623 select_horiz_by_yawpitch
625 // create message and add to list
626 ActiveObjectMessage aom(getId(), true, str);
627 m_messages_out.push(aom);
630 std::string LuaEntitySAO::getName()
635 std::string LuaEntitySAO::getPropertyPacket()
637 return gob_cmd_set_properties(m_prop);
640 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
642 // If the object is attached client-side, don't waste bandwidth sending its position to clients
646 m_last_sent_move_precision = m_base_position.getDistanceFrom(
647 m_last_sent_position);
648 m_last_sent_position_timer = 0;
649 m_last_sent_yaw = m_yaw;
650 m_last_sent_position = m_base_position;
651 m_last_sent_velocity = m_velocity;
652 //m_last_sent_acceleration = m_acceleration;
654 float update_interval = m_env->getSendRecommendedInterval();
656 std::string str = gob_cmd_update_position(
665 // create message and add to list
666 ActiveObjectMessage aom(getId(), false, str);
667 m_messages_out.push(aom);
670 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
673 //update collision box
674 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
675 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
677 toset->MinEdge += m_base_position;
678 toset->MaxEdge += m_base_position;
686 bool LuaEntitySAO::collideWithObjects(){
687 return m_prop.collideWithObjects;
694 // No prototype, PlayerSAO does not need to be deserialized
696 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
697 const std::set<std::string> &privs, bool is_singleplayer):
698 ServerActiveObject(env_, v3f(0,0,0)),
703 m_last_good_position(0,0,0),
704 m_time_from_last_punch(0),
705 m_nocheat_dig_pos(32767, 32767, 32767),
706 m_nocheat_dig_time(0),
708 m_position_not_sent(false),
709 m_armor_groups_sent(false),
710 m_properties_sent(true),
712 m_is_singleplayer(is_singleplayer),
713 m_animation_speed(0),
714 m_animation_blend(0),
715 m_animation_sent(false),
716 m_bone_position_sent(false),
717 m_attachment_parent_id(0),
718 m_attachment_sent(false),
720 m_physics_override_speed(1),
721 m_physics_override_jump(1),
722 m_physics_override_gravity(1),
723 m_physics_override_sneak(true),
724 m_physics_override_sneak_glitch(true),
725 m_physics_override_sent(false)
727 assert(m_player); // pre-condition
728 assert(m_peer_id != 0); // pre-condition
729 setBasePosition(m_player->getPosition());
730 m_inventory = &m_player->inventory;
731 m_armor_groups["fleshy"] = 100;
733 m_prop.hp_max = PLAYER_MAX_HP;
734 m_prop.physical = false;
736 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
737 // start of default appearance, this should be overwritten by LUA
738 m_prop.visual = "upright_sprite";
739 m_prop.visual_size = v2f(1, 2);
740 m_prop.textures.clear();
741 m_prop.textures.push_back("player.png");
742 m_prop.textures.push_back("player_back.png");
743 m_prop.colors.clear();
744 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
745 m_prop.spritediv = v2s16(1,1);
746 // end of default appearance
747 m_prop.is_visible = true;
748 m_prop.makes_footstep_sound = true;
751 PlayerSAO::~PlayerSAO()
753 if(m_inventory != &m_player->inventory)
758 std::string PlayerSAO::getDescription()
760 return std::string("player ") + m_player->getName();
763 // Called after id has been set and has been inserted in environment
764 void PlayerSAO::addedToEnvironment(u32 dtime_s)
766 ServerActiveObject::addedToEnvironment(dtime_s);
767 ServerActiveObject::setBasePosition(m_player->getPosition());
768 m_player->setPlayerSAO(this);
769 m_player->peer_id = m_peer_id;
770 m_last_good_position = m_player->getPosition();
773 // Called before removing from environment
774 void PlayerSAO::removingFromEnvironment()
776 ServerActiveObject::removingFromEnvironment();
777 if(m_player->getPlayerSAO() == this)
779 m_player->setPlayerSAO(NULL);
780 m_player->peer_id = 0;
781 m_env->savePlayer(m_player->getName());
782 m_env->removePlayer(m_player->getName());
786 bool PlayerSAO::isStaticAllowed() const
791 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
793 std::ostringstream os(std::ios::binary);
795 if(protocol_version >= 15)
797 writeU8(os, 1); // version
798 os<<serializeString(m_player->getName()); // name
799 writeU8(os, 1); // is_player
800 writeS16(os, getId()); //id
801 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
802 writeF1000(os, m_player->getYaw());
803 writeS16(os, getHP());
805 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
806 os<<serializeLongString(getPropertyPacket()); // message 1
807 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
808 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
809 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
810 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
812 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
813 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
814 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
815 m_physics_override_sneak_glitch)); // 5
819 writeU8(os, 0); // version
820 os<<serializeString(m_player->getName()); // name
821 writeU8(os, 1); // is_player
822 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
823 writeF1000(os, m_player->getYaw());
824 writeS16(os, getHP());
825 writeU8(os, 2); // number of messages stuffed in here
826 os<<serializeLongString(getPropertyPacket()); // message 1
827 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
834 std::string PlayerSAO::getStaticData()
836 FATAL_ERROR("Deprecated function (?)");
840 bool PlayerSAO::isAttached()
842 if(!m_attachment_parent_id)
844 // Check if the parent still exists
845 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
851 void PlayerSAO::step(float dtime, bool send_recommended)
853 if(!m_properties_sent)
855 m_properties_sent = true;
856 std::string str = getPropertyPacket();
857 // create message and add to list
858 ActiveObjectMessage aom(getId(), true, str);
859 m_messages_out.push(aom);
862 // If attached, check that our parent is still there. If it isn't, detach.
863 if(m_attachment_parent_id && !isAttached())
865 m_attachment_parent_id = 0;
866 m_attachment_bone = "";
867 m_attachment_position = v3f(0,0,0);
868 m_attachment_rotation = v3f(0,0,0);
869 m_player->setPosition(m_last_good_position);
870 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
873 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
875 // Set lag pool maximums based on estimated lag
876 const float LAG_POOL_MIN = 5.0;
877 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
878 if(lag_pool_max < LAG_POOL_MIN)
879 lag_pool_max = LAG_POOL_MIN;
880 m_dig_pool.setMax(lag_pool_max);
881 m_move_pool.setMax(lag_pool_max);
883 // Increment cheat prevention timers
884 m_dig_pool.add(dtime);
885 m_move_pool.add(dtime);
886 m_time_from_last_punch += dtime;
887 m_nocheat_dig_time += dtime;
889 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
890 // If the object gets detached this comes into effect automatically from the last known origin
893 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
894 m_last_good_position = pos;
895 m_player->setPosition(pos);
898 if(send_recommended == false)
901 // If the object is attached client-side, don't waste bandwidth sending its position to clients
902 if(m_position_not_sent && !isAttached())
904 m_position_not_sent = false;
905 float update_interval = m_env->getSendRecommendedInterval();
907 if(isAttached()) // Just in case we ever do send attachment position too
908 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
910 pos = m_player->getPosition() + v3f(0,BS*1,0);
911 std::string str = gob_cmd_update_position(
920 // create message and add to list
921 ActiveObjectMessage aom(getId(), false, str);
922 m_messages_out.push(aom);
925 if(m_armor_groups_sent == false) {
926 m_armor_groups_sent = true;
927 std::string str = gob_cmd_update_armor_groups(
929 // create message and add to list
930 ActiveObjectMessage aom(getId(), true, str);
931 m_messages_out.push(aom);
934 if(m_physics_override_sent == false){
935 m_physics_override_sent = true;
936 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
937 m_physics_override_jump, m_physics_override_gravity,
938 m_physics_override_sneak, m_physics_override_sneak_glitch);
939 // create message and add to list
940 ActiveObjectMessage aom(getId(), true, str);
941 m_messages_out.push(aom);
944 if(m_animation_sent == false){
945 m_animation_sent = true;
946 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
947 // create message and add to list
948 ActiveObjectMessage aom(getId(), true, str);
949 m_messages_out.push(aom);
952 if(m_bone_position_sent == false){
953 m_bone_position_sent = true;
954 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
955 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
956 // create message and add to list
957 ActiveObjectMessage aom(getId(), true, str);
958 m_messages_out.push(aom);
962 if(m_attachment_sent == false){
963 m_attachment_sent = true;
964 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
965 // create message and add to list
966 ActiveObjectMessage aom(getId(), true, str);
967 m_messages_out.push(aom);
971 void PlayerSAO::setBasePosition(const v3f &position)
973 // This needs to be ran for attachments too
974 ServerActiveObject::setBasePosition(position);
975 m_position_not_sent = true;
978 void PlayerSAO::setPos(v3f pos)
982 m_player->setPosition(pos);
983 // Movement caused by this command is always valid
984 m_last_good_position = pos;
985 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
988 void PlayerSAO::moveTo(v3f pos, bool continuous)
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::setYaw(float yaw)
1000 m_player->setYaw(yaw);
1001 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1004 void PlayerSAO::setPitch(float pitch)
1006 m_player->setPitch(pitch);
1007 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1010 int PlayerSAO::punch(v3f dir,
1011 const ToolCapabilities *toolcap,
1012 ServerActiveObject *puncher,
1013 float time_from_last_punch)
1015 // It's best that attachments cannot be punched
1022 // No effect if PvP disabled
1023 if(g_settings->getBool("enable_pvp") == false){
1024 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1025 std::string str = gob_cmd_punched(0, getHP());
1026 // create message and add to list
1027 ActiveObjectMessage aom(getId(), true, str);
1028 m_messages_out.push(aom);
1033 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1034 time_from_last_punch);
1036 std::string punchername = "nil";
1039 punchername = puncher->getDescription();
1041 actionstream<<"Player "<<m_player->getName()<<" punched by "
1042 <<punchername<<", damage "<<hitparams.hp
1045 setHP(getHP() - hitparams.hp);
1047 return hitparams.wear;
1050 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1054 s16 PlayerSAO::getHP() const
1056 return m_player->hp;
1059 s16 PlayerSAO::readDamage()
1061 s16 damage = m_damage;
1066 void PlayerSAO::setHP(s16 hp)
1068 s16 oldhp = m_player->hp;
1072 else if (hp > PLAYER_MAX_HP)
1075 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1082 m_damage += (oldhp - hp);
1084 // Update properties on death
1085 if ((hp == 0) != (oldhp == 0))
1086 m_properties_sent = false;
1089 u16 PlayerSAO::getBreath() const
1091 return m_player->getBreath();
1094 void PlayerSAO::setBreath(u16 breath)
1096 m_player->setBreath(breath);
1099 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1101 m_armor_groups = armor_groups;
1102 m_armor_groups_sent = false;
1105 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1107 // store these so they can be updated to clients
1108 m_animation_range = frame_range;
1109 m_animation_speed = frame_speed;
1110 m_animation_blend = frame_blend;
1111 m_animation_sent = false;
1114 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1116 // store these so they can be updated to clients
1117 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1118 m_bone_position_sent = false;
1121 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1123 // Attachments need to be handled on both the server and client.
1124 // If we just attach on the server, we can only copy the position of the parent. Attachments
1125 // are still sent to clients at an interval so players might see them lagging, plus we can't
1126 // read and attach to skeletal bones.
1127 // If we just attach on the client, the server still sees the child at its original location.
1128 // This breaks some things so we also give the server the most accurate representation
1129 // even if players only see the client changes.
1131 m_attachment_parent_id = parent_id;
1132 m_attachment_bone = bone;
1133 m_attachment_position = position;
1134 m_attachment_rotation = rotation;
1135 m_attachment_sent = false;
1138 ObjectProperties* PlayerSAO::accessObjectProperties()
1143 void PlayerSAO::notifyObjectPropertiesModified()
1145 m_properties_sent = false;
1148 Inventory* PlayerSAO::getInventory()
1152 const Inventory* PlayerSAO::getInventory() const
1157 InventoryLocation PlayerSAO::getInventoryLocation() const
1159 InventoryLocation loc;
1160 loc.setPlayer(m_player->getName());
1164 std::string PlayerSAO::getWieldList() const
1169 int PlayerSAO::getWieldIndex() const
1171 return m_wield_index;
1174 void PlayerSAO::setWieldIndex(int i)
1176 if(i != m_wield_index) {
1181 void PlayerSAO::disconnected()
1185 if(m_player->getPlayerSAO() == this)
1187 m_player->setPlayerSAO(NULL);
1188 m_player->peer_id = 0;
1192 std::string PlayerSAO::getPropertyPacket()
1194 m_prop.is_visible = (true);
1195 return gob_cmd_set_properties(m_prop);
1198 bool PlayerSAO::checkMovementCheat()
1200 bool cheated = false;
1201 if(isAttached() || m_is_singleplayer ||
1202 g_settings->getBool("disable_anticheat"))
1204 m_last_good_position = m_player->getPosition();
1209 Check player movements
1211 NOTE: Actually the server should handle player physics like the
1212 client does and compare player's position to what is calculated
1213 on our side. This is required when eg. players fly due to an
1214 explosion. Altough a node-based alternative might be possible
1215 too, and much more lightweight.
1218 float player_max_speed = 0;
1219 if(m_privs.count("fast") != 0){
1221 player_max_speed = m_player->movement_speed_fast;
1224 player_max_speed = m_player->movement_speed_walk;
1226 // Tolerance. With the lag pool we shouldn't need it.
1227 //player_max_speed *= 2.5;
1228 //player_max_speed_up *= 2.5;
1230 v3f diff = (m_player->getPosition() - m_last_good_position);
1231 float d_vert = diff.Y;
1233 float d_horiz = diff.getLength();
1234 float required_time = d_horiz/player_max_speed;
1235 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1236 required_time = d_vert/player_max_speed;
1237 if(m_move_pool.grab(required_time)){
1238 m_last_good_position = m_player->getPosition();
1240 actionstream<<"Player "<<m_player->getName()
1241 <<" moved too fast; resetting position"
1243 m_player->setPosition(m_last_good_position);
1250 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1251 //update collision box
1252 *toset = m_player->getCollisionbox();
1254 toset->MinEdge += m_base_position;
1255 toset->MaxEdge += m_base_position;
1260 bool PlayerSAO::collideWithObjects(){