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_loop(true),
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(
329 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
330 // create message and add to list
331 ActiveObjectMessage aom(getId(), true, str);
332 m_messages_out.push(aom);
335 if(m_bone_position_sent == false){
336 m_bone_position_sent = true;
337 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
338 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
339 // create message and add to list
340 ActiveObjectMessage aom(getId(), true, str);
341 m_messages_out.push(aom);
345 if(m_attachment_sent == false){
346 m_attachment_sent = true;
347 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
348 // create message and add to list
349 ActiveObjectMessage aom(getId(), true, str);
350 m_messages_out.push(aom);
354 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
356 std::ostringstream os(std::ios::binary);
358 if(protocol_version >= 14)
360 writeU8(os, 1); // version
361 os<<serializeString(""); // name
362 writeU8(os, 0); // is_player
363 writeS16(os, getId()); //id
364 writeV3F1000(os, m_base_position);
365 writeF1000(os, m_yaw);
368 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
369 os<<serializeLongString(getPropertyPacket()); // message 1
370 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
371 os<<serializeLongString(gob_cmd_update_animation(
372 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
373 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
374 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
376 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
380 writeU8(os, 0); // version
381 os<<serializeString(""); // name
382 writeU8(os, 0); // is_player
383 writeV3F1000(os, m_base_position);
384 writeF1000(os, m_yaw);
386 writeU8(os, 2); // number of messages stuffed in here
387 os<<serializeLongString(getPropertyPacket()); // message 1
388 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
395 std::string LuaEntitySAO::getStaticData()
397 verbosestream<<__FUNCTION_NAME<<std::endl;
398 std::ostringstream os(std::ios::binary);
402 os<<serializeString(m_init_name);
405 std::string state = m_env->getScriptIface()->
406 luaentity_GetStaticdata(m_id);
407 os<<serializeLongString(state);
409 os<<serializeLongString(m_init_state);
414 writeV3F1000(os, m_velocity);
416 writeF1000(os, m_yaw);
420 int LuaEntitySAO::punch(v3f dir,
421 const ToolCapabilities *toolcap,
422 ServerActiveObject *puncher,
423 float time_from_last_punch)
426 // Delete unknown LuaEntities when punched
431 // It's best that attachments cannot be punched
435 ItemStack *punchitem = NULL;
436 ItemStack punchitem_static;
438 punchitem_static = puncher->getWieldedItem();
439 punchitem = &punchitem_static;
442 PunchDamageResult result = getPunchDamage(
446 time_from_last_punch);
448 if (result.did_punch) {
449 setHP(getHP() - result.damage);
451 if (result.damage > 0) {
452 std::string punchername = puncher ? puncher->getDescription() : "nil";
454 actionstream << getDescription() << " punched by "
455 << punchername << ", damage " << result.damage
456 << " hp, health now " << getHP() << " hp" << std::endl;
459 std::string str = gob_cmd_punched(result.damage, getHP());
460 // create message and add to list
461 ActiveObjectMessage aom(getId(), true, str);
462 m_messages_out.push(aom);
468 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
469 time_from_last_punch, toolcap, dir);
474 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
478 // It's best that attachments cannot be clicked
481 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
484 void LuaEntitySAO::setPos(v3f pos)
488 m_base_position = pos;
489 sendPosition(false, true);
492 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
496 m_base_position = pos;
498 sendPosition(true, true);
501 float LuaEntitySAO::getMinimumSavedMovement()
506 std::string LuaEntitySAO::getDescription()
508 std::ostringstream os(std::ios::binary);
509 os<<"LuaEntitySAO at (";
510 os<<(m_base_position.X/BS)<<",";
511 os<<(m_base_position.Y/BS)<<",";
512 os<<(m_base_position.Z/BS);
517 void LuaEntitySAO::setHP(s16 hp)
523 s16 LuaEntitySAO::getHP() const
528 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
530 m_armor_groups = armor_groups;
531 m_armor_groups_sent = false;
534 ItemGroupList LuaEntitySAO::getArmorGroups()
536 return m_armor_groups;
539 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
541 m_animation_range = frame_range;
542 m_animation_speed = frame_speed;
543 m_animation_blend = frame_blend;
544 m_animation_loop = frame_loop;
545 m_animation_sent = false;
548 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
550 *frame_range = m_animation_range;
551 *frame_speed = m_animation_speed;
552 *frame_blend = m_animation_blend;
553 *frame_loop = m_animation_loop;
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 void LuaEntitySAO::addAttachmentChild(int child_id)
596 m_attachment_child_ids.insert(child_id);
599 void LuaEntitySAO::removeAttachmentChild(int child_id)
601 m_attachment_child_ids.erase(child_id);
604 std::set<int> LuaEntitySAO::getAttachmentChildIds()
606 return m_attachment_child_ids;
609 ObjectProperties* LuaEntitySAO::accessObjectProperties()
614 void LuaEntitySAO::notifyObjectPropertiesModified()
616 m_properties_sent = false;
619 void LuaEntitySAO::setVelocity(v3f velocity)
621 m_velocity = velocity;
624 v3f LuaEntitySAO::getVelocity()
629 void LuaEntitySAO::setAcceleration(v3f acceleration)
631 m_acceleration = acceleration;
634 v3f LuaEntitySAO::getAcceleration()
636 return m_acceleration;
639 void LuaEntitySAO::setYaw(float yaw)
644 float LuaEntitySAO::getYaw()
649 void LuaEntitySAO::setTextureMod(const std::string &mod)
651 std::string str = gob_cmd_set_texture_mod(mod);
652 // create message and add to list
653 ActiveObjectMessage aom(getId(), true, str);
654 m_messages_out.push(aom);
657 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
658 bool select_horiz_by_yawpitch)
660 std::string str = gob_cmd_set_sprite(
664 select_horiz_by_yawpitch
666 // create message and add to list
667 ActiveObjectMessage aom(getId(), true, str);
668 m_messages_out.push(aom);
671 std::string LuaEntitySAO::getName()
676 std::string LuaEntitySAO::getPropertyPacket()
678 return gob_cmd_set_properties(m_prop);
681 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
683 // If the object is attached client-side, don't waste bandwidth sending its position to clients
687 m_last_sent_move_precision = m_base_position.getDistanceFrom(
688 m_last_sent_position);
689 m_last_sent_position_timer = 0;
690 m_last_sent_yaw = m_yaw;
691 m_last_sent_position = m_base_position;
692 m_last_sent_velocity = m_velocity;
693 //m_last_sent_acceleration = m_acceleration;
695 float update_interval = m_env->getSendRecommendedInterval();
697 std::string str = gob_cmd_update_position(
706 // create message and add to list
707 ActiveObjectMessage aom(getId(), false, str);
708 m_messages_out.push(aom);
711 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
714 //update collision box
715 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
716 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
718 toset->MinEdge += m_base_position;
719 toset->MaxEdge += m_base_position;
727 bool LuaEntitySAO::collideWithObjects(){
728 return m_prop.collideWithObjects;
735 // No prototype, PlayerSAO does not need to be deserialized
737 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
738 const std::set<std::string> &privs, bool is_singleplayer):
739 ServerActiveObject(env_, v3f(0,0,0)),
744 m_last_good_position(0,0,0),
745 m_time_from_last_punch(0),
746 m_nocheat_dig_pos(32767, 32767, 32767),
747 m_nocheat_dig_time(0),
749 m_position_not_sent(false),
750 m_armor_groups_sent(false),
751 m_properties_sent(true),
753 m_is_singleplayer(is_singleplayer),
754 m_animation_speed(0),
755 m_animation_blend(0),
756 m_animation_loop(true),
757 m_animation_sent(false),
758 m_bone_position_sent(false),
759 m_attachment_parent_id(0),
760 m_attachment_sent(false),
761 m_nametag_color(video::SColor(255, 255, 255, 255)),
762 m_nametag_sent(false),
764 m_physics_override_speed(1),
765 m_physics_override_jump(1),
766 m_physics_override_gravity(1),
767 m_physics_override_sneak(true),
768 m_physics_override_sneak_glitch(true),
769 m_physics_override_sent(false)
771 assert(m_player); // pre-condition
772 assert(m_peer_id != 0); // pre-condition
773 setBasePosition(m_player->getPosition());
774 m_inventory = &m_player->inventory;
775 m_armor_groups["fleshy"] = 100;
777 m_prop.hp_max = PLAYER_MAX_HP;
778 m_prop.physical = false;
780 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
781 // start of default appearance, this should be overwritten by LUA
782 m_prop.visual = "upright_sprite";
783 m_prop.visual_size = v2f(1, 2);
784 m_prop.textures.clear();
785 m_prop.textures.push_back("player.png");
786 m_prop.textures.push_back("player_back.png");
787 m_prop.colors.clear();
788 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
789 m_prop.spritediv = v2s16(1,1);
790 // end of default appearance
791 m_prop.is_visible = true;
792 m_prop.makes_footstep_sound = true;
795 PlayerSAO::~PlayerSAO()
797 if(m_inventory != &m_player->inventory)
802 std::string PlayerSAO::getDescription()
804 return std::string("player ") + m_player->getName();
807 // Called after id has been set and has been inserted in environment
808 void PlayerSAO::addedToEnvironment(u32 dtime_s)
810 ServerActiveObject::addedToEnvironment(dtime_s);
811 ServerActiveObject::setBasePosition(m_player->getPosition());
812 m_player->setPlayerSAO(this);
813 m_player->peer_id = m_peer_id;
814 m_last_good_position = m_player->getPosition();
817 // Called before removing from environment
818 void PlayerSAO::removingFromEnvironment()
820 ServerActiveObject::removingFromEnvironment();
821 if(m_player->getPlayerSAO() == this)
823 m_player->setPlayerSAO(NULL);
824 m_player->peer_id = 0;
825 m_env->savePlayer(m_player->getName());
826 m_env->removePlayer(m_player->getName());
830 bool PlayerSAO::isStaticAllowed() const
835 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
837 std::ostringstream os(std::ios::binary);
839 if(protocol_version >= 15)
841 writeU8(os, 1); // version
842 os<<serializeString(m_player->getName()); // name
843 writeU8(os, 1); // is_player
844 writeS16(os, getId()); //id
845 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
846 writeF1000(os, m_player->getYaw());
847 writeS16(os, getHP());
849 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
850 os<<serializeLongString(getPropertyPacket()); // message 1
851 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
852 os<<serializeLongString(gob_cmd_update_animation(
853 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
854 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
855 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
857 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
858 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
859 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
860 m_physics_override_sneak_glitch)); // 5
861 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
865 writeU8(os, 0); // version
866 os<<serializeString(m_player->getName()); // name
867 writeU8(os, 1); // is_player
868 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
869 writeF1000(os, m_player->getYaw());
870 writeS16(os, getHP());
871 writeU8(os, 2); // number of messages stuffed in here
872 os<<serializeLongString(getPropertyPacket()); // message 1
873 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
880 std::string PlayerSAO::getStaticData()
882 FATAL_ERROR("Deprecated function (?)");
886 bool PlayerSAO::isAttached()
888 if(!m_attachment_parent_id)
890 // Check if the parent still exists
891 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
897 void PlayerSAO::step(float dtime, bool send_recommended)
899 if(!m_properties_sent)
901 m_properties_sent = true;
902 std::string str = getPropertyPacket();
903 // create message and add to list
904 ActiveObjectMessage aom(getId(), true, str);
905 m_messages_out.push(aom);
908 // If attached, check that our parent is still there. If it isn't, detach.
909 if(m_attachment_parent_id && !isAttached())
911 m_attachment_parent_id = 0;
912 m_attachment_bone = "";
913 m_attachment_position = v3f(0,0,0);
914 m_attachment_rotation = v3f(0,0,0);
915 m_player->setPosition(m_last_good_position);
916 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
919 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
921 // Set lag pool maximums based on estimated lag
922 const float LAG_POOL_MIN = 5.0;
923 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
924 if(lag_pool_max < LAG_POOL_MIN)
925 lag_pool_max = LAG_POOL_MIN;
926 m_dig_pool.setMax(lag_pool_max);
927 m_move_pool.setMax(lag_pool_max);
929 // Increment cheat prevention timers
930 m_dig_pool.add(dtime);
931 m_move_pool.add(dtime);
932 m_time_from_last_punch += dtime;
933 m_nocheat_dig_time += dtime;
935 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
936 // If the object gets detached this comes into effect automatically from the last known origin
939 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
940 m_last_good_position = pos;
941 m_player->setPosition(pos);
944 if(send_recommended == false)
947 // If the object is attached client-side, don't waste bandwidth sending its position to clients
948 if(m_position_not_sent && !isAttached())
950 m_position_not_sent = false;
951 float update_interval = m_env->getSendRecommendedInterval();
953 if(isAttached()) // Just in case we ever do send attachment position too
954 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
956 pos = m_player->getPosition() + v3f(0,BS*1,0);
957 std::string str = gob_cmd_update_position(
966 // create message and add to list
967 ActiveObjectMessage aom(getId(), false, str);
968 m_messages_out.push(aom);
971 if(m_armor_groups_sent == false) {
972 m_armor_groups_sent = true;
973 std::string str = gob_cmd_update_armor_groups(
975 // create message and add to list
976 ActiveObjectMessage aom(getId(), true, str);
977 m_messages_out.push(aom);
980 if(m_physics_override_sent == false){
981 m_physics_override_sent = true;
982 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
983 m_physics_override_jump, m_physics_override_gravity,
984 m_physics_override_sneak, m_physics_override_sneak_glitch);
985 // create message and add to list
986 ActiveObjectMessage aom(getId(), true, str);
987 m_messages_out.push(aom);
990 if(m_animation_sent == false){
991 m_animation_sent = true;
992 std::string str = gob_cmd_update_animation(
993 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
994 // create message and add to list
995 ActiveObjectMessage aom(getId(), true, str);
996 m_messages_out.push(aom);
999 if(m_bone_position_sent == false){
1000 m_bone_position_sent = true;
1001 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1002 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1003 // create message and add to list
1004 ActiveObjectMessage aom(getId(), true, str);
1005 m_messages_out.push(aom);
1009 if(m_attachment_sent == false){
1010 m_attachment_sent = true;
1011 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1012 // create message and add to list
1013 ActiveObjectMessage aom(getId(), true, str);
1014 m_messages_out.push(aom);
1017 if (m_nametag_sent == false) {
1018 m_nametag_sent = true;
1019 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
1020 // create message and add to list
1021 ActiveObjectMessage aom(getId(), true, str);
1022 m_messages_out.push(aom);
1026 void PlayerSAO::setBasePosition(const v3f &position)
1028 // This needs to be ran for attachments too
1029 ServerActiveObject::setBasePosition(position);
1030 m_position_not_sent = true;
1033 void PlayerSAO::setPos(v3f pos)
1037 m_player->setPosition(pos);
1038 // Movement caused by this command is always valid
1039 m_last_good_position = pos;
1040 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1043 void PlayerSAO::moveTo(v3f pos, bool continuous)
1047 m_player->setPosition(pos);
1048 // Movement caused by this command is always valid
1049 m_last_good_position = pos;
1050 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1053 void PlayerSAO::setYaw(float yaw)
1055 m_player->setYaw(yaw);
1056 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1059 void PlayerSAO::setPitch(float pitch)
1061 m_player->setPitch(pitch);
1062 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1065 int PlayerSAO::punch(v3f dir,
1066 const ToolCapabilities *toolcap,
1067 ServerActiveObject *puncher,
1068 float time_from_last_punch)
1070 // It's best that attachments cannot be punched
1077 // No effect if PvP disabled
1078 if (g_settings->getBool("enable_pvp") == false) {
1079 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1080 std::string str = gob_cmd_punched(0, getHP());
1081 // create message and add to list
1082 ActiveObjectMessage aom(getId(), true, str);
1083 m_messages_out.push(aom);
1088 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1089 time_from_last_punch);
1091 std::string punchername = "nil";
1094 punchername = puncher->getDescription();
1096 PlayerSAO *playersao = m_player->getPlayerSAO();
1098 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1099 puncher, time_from_last_punch, toolcap, dir,
1102 if (!damage_handled) {
1103 setHP(getHP() - hitparams.hp);
1104 } else { // override client prediction
1105 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1106 std::string str = gob_cmd_punched(0, getHP());
1107 // create message and add to list
1108 ActiveObjectMessage aom(getId(), true, str);
1109 m_messages_out.push(aom);
1114 actionstream << "Player " << m_player->getName() << " punched by "
1116 if (!damage_handled) {
1117 actionstream << ", damage " << hitparams.hp << " HP";
1119 actionstream << ", damage handled by lua";
1121 actionstream << std::endl;
1123 return hitparams.wear;
1126 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1130 s16 PlayerSAO::getHP() const
1132 return m_player->hp;
1135 s16 PlayerSAO::readDamage()
1137 s16 damage = m_damage;
1142 void PlayerSAO::setHP(s16 hp)
1144 s16 oldhp = m_player->hp;
1146 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1150 hp = oldhp + hp_change;
1154 else if (hp > PLAYER_MAX_HP)
1157 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1164 m_damage += (oldhp - hp);
1166 // Update properties on death
1167 if ((hp == 0) != (oldhp == 0))
1168 m_properties_sent = false;
1171 u16 PlayerSAO::getBreath() const
1173 return m_player->getBreath();
1176 void PlayerSAO::setBreath(u16 breath)
1178 m_player->setBreath(breath);
1181 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1183 m_armor_groups = armor_groups;
1184 m_armor_groups_sent = false;
1187 ItemGroupList PlayerSAO::getArmorGroups()
1189 return m_armor_groups;
1192 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1194 // store these so they can be updated to clients
1195 m_animation_range = frame_range;
1196 m_animation_speed = frame_speed;
1197 m_animation_blend = frame_blend;
1198 m_animation_loop = frame_loop;
1199 m_animation_sent = false;
1202 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1204 *frame_range = m_animation_range;
1205 *frame_speed = m_animation_speed;
1206 *frame_blend = m_animation_blend;
1207 *frame_loop = m_animation_loop;
1210 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1212 // store these so they can be updated to clients
1213 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1214 m_bone_position_sent = false;
1217 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1219 *position = m_bone_position[bone].X;
1220 *rotation = m_bone_position[bone].Y;
1223 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1225 // Attachments need to be handled on both the server and client.
1226 // If we just attach on the server, we can only copy the position of the parent. Attachments
1227 // are still sent to clients at an interval so players might see them lagging, plus we can't
1228 // read and attach to skeletal bones.
1229 // If we just attach on the client, the server still sees the child at its original location.
1230 // This breaks some things so we also give the server the most accurate representation
1231 // even if players only see the client changes.
1233 m_attachment_parent_id = parent_id;
1234 m_attachment_bone = bone;
1235 m_attachment_position = position;
1236 m_attachment_rotation = rotation;
1237 m_attachment_sent = false;
1240 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1243 *parent_id = m_attachment_parent_id;
1244 *bone = m_attachment_bone;
1245 *position = m_attachment_position;
1246 *rotation = m_attachment_rotation;
1249 void PlayerSAO::addAttachmentChild(int child_id)
1251 m_attachment_child_ids.insert(child_id);
1254 void PlayerSAO::removeAttachmentChild(int child_id)
1256 m_attachment_child_ids.erase(child_id);
1259 std::set<int> PlayerSAO::getAttachmentChildIds()
1261 return m_attachment_child_ids;
1264 ObjectProperties* PlayerSAO::accessObjectProperties()
1269 void PlayerSAO::notifyObjectPropertiesModified()
1271 m_properties_sent = false;
1274 void PlayerSAO::setNametagColor(video::SColor color)
1276 m_nametag_color = color;
1277 m_nametag_sent = false;
1280 video::SColor PlayerSAO::getNametagColor()
1282 return m_nametag_color;
1285 Inventory* PlayerSAO::getInventory()
1289 const Inventory* PlayerSAO::getInventory() const
1294 InventoryLocation PlayerSAO::getInventoryLocation() const
1296 InventoryLocation loc;
1297 loc.setPlayer(m_player->getName());
1301 std::string PlayerSAO::getWieldList() const
1306 int PlayerSAO::getWieldIndex() const
1308 return m_wield_index;
1311 void PlayerSAO::setWieldIndex(int i)
1313 if(i != m_wield_index) {
1318 void PlayerSAO::disconnected()
1322 if(m_player->getPlayerSAO() == this)
1324 m_player->setPlayerSAO(NULL);
1325 m_player->peer_id = 0;
1329 std::string PlayerSAO::getPropertyPacket()
1331 m_prop.is_visible = (true);
1332 return gob_cmd_set_properties(m_prop);
1335 bool PlayerSAO::checkMovementCheat()
1337 bool cheated = false;
1338 if(isAttached() || m_is_singleplayer ||
1339 g_settings->getBool("disable_anticheat"))
1341 m_last_good_position = m_player->getPosition();
1346 Check player movements
1348 NOTE: Actually the server should handle player physics like the
1349 client does and compare player's position to what is calculated
1350 on our side. This is required when eg. players fly due to an
1351 explosion. Altough a node-based alternative might be possible
1352 too, and much more lightweight.
1355 float player_max_speed = 0;
1356 if(m_privs.count("fast") != 0){
1358 player_max_speed = m_player->movement_speed_fast;
1361 player_max_speed = m_player->movement_speed_walk;
1363 // Tolerance. With the lag pool we shouldn't need it.
1364 //player_max_speed *= 2.5;
1365 //player_max_speed_up *= 2.5;
1367 v3f diff = (m_player->getPosition() - m_last_good_position);
1368 float d_vert = diff.Y;
1370 float d_horiz = diff.getLength();
1371 float required_time = d_horiz/player_max_speed;
1372 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1373 required_time = d_vert/player_max_speed;
1374 if(m_move_pool.grab(required_time)){
1375 m_last_good_position = m_player->getPosition();
1377 actionstream<<"Player "<<m_player->getName()
1378 <<" moved too fast; resetting position"
1380 m_player->setPosition(m_last_good_position);
1387 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1388 //update collision box
1389 *toset = m_player->getCollisionbox();
1391 toset->MinEdge += m_base_position;
1392 toset->MaxEdge += m_base_position;
1397 bool PlayerSAO::collideWithObjects(){