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 "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
31 #include "scripting_game.h"
32 #include "genericobject.h"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class TestSAO : public ServerActiveObject
44 TestSAO(ServerEnvironment *env, v3f pos):
45 ServerActiveObject(env, pos),
49 ServerActiveObject::registerType(getType(), create);
51 ActiveObjectType getType() const
52 { return ACTIVEOBJECT_TYPE_TEST; }
54 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55 const std::string &data)
57 return new TestSAO(env, pos);
60 void step(float dtime, bool send_recommended)
69 m_base_position.Y += dtime * BS * 2;
70 if(m_base_position.Y > 8*BS)
71 m_base_position.Y = 2*BS;
73 if(send_recommended == false)
83 data += itos(0); // 0 = position
85 data += itos(m_base_position.X);
87 data += itos(m_base_position.Y);
89 data += itos(m_base_position.Z);
91 ActiveObjectMessage aom(getId(), false, data);
92 m_messages_out.push(aom);
96 bool getCollisionBox(aabb3f *toset) {
100 bool collideWithObjects() {
109 // Prototype (registers item for deserialization)
110 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
116 // Prototype (registers item for deserialization)
117 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
119 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
120 const std::string &name, const std::string &state):
121 ServerActiveObject(env, pos),
127 m_acceleration(0,0,0),
129 m_properties_sent(true),
131 m_last_sent_position(0,0,0),
132 m_last_sent_velocity(0,0,0),
133 m_last_sent_position_timer(0),
134 m_last_sent_move_precision(0),
135 m_armor_groups_sent(false),
136 m_animation_speed(0),
137 m_animation_blend(0),
138 m_animation_loop(true),
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);
179 m_prop.infotext = m_init_name;
183 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
184 const std::string &data)
192 std::istringstream is(data, std::ios::binary);
194 u8 version = readU8(is);
195 // check if version is supported
197 name = deSerializeString(is);
198 state = deSerializeLongString(is);
200 else if(version == 1){
201 name = deSerializeString(is);
202 state = deSerializeLongString(is);
204 velocity = readV3F1000(is);
209 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
210 <<state<<"\")"<<std::endl;
211 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
213 sao->m_velocity = velocity;
218 bool LuaEntitySAO::isAttached()
220 if(!m_attachment_parent_id)
222 // Check if the parent still exists
223 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
229 void LuaEntitySAO::step(float dtime, bool send_recommended)
231 if(!m_properties_sent)
233 m_properties_sent = true;
234 std::string str = getPropertyPacket();
235 // create message and add to list
236 ActiveObjectMessage aom(getId(), true, str);
237 m_messages_out.push(aom);
240 // If attached, check that our parent is still there. If it isn't, detach.
241 if(m_attachment_parent_id && !isAttached())
243 m_attachment_parent_id = 0;
244 m_attachment_bone = "";
245 m_attachment_position = v3f(0,0,0);
246 m_attachment_rotation = v3f(0,0,0);
247 sendPosition(false, true);
250 m_last_sent_position_timer += dtime;
252 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
253 // If the object gets detached this comes into effect automatically from the last known origin
256 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
257 m_base_position = pos;
258 m_velocity = v3f(0,0,0);
259 m_acceleration = v3f(0,0,0);
264 aabb3f box = m_prop.collisionbox;
267 collisionMoveResult moveresult;
268 f32 pos_max_d = BS*0.25; // Distance per iteration
269 v3f p_pos = m_base_position;
270 v3f p_velocity = m_velocity;
271 v3f p_acceleration = m_acceleration;
272 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
273 pos_max_d, box, m_prop.stepheight, dtime,
274 &p_pos, &p_velocity, p_acceleration,
275 this, m_prop.collideWithObjects);
278 m_base_position = p_pos;
279 m_velocity = p_velocity;
280 m_acceleration = p_acceleration;
282 m_base_position += dtime * m_velocity + 0.5 * dtime
283 * dtime * m_acceleration;
284 m_velocity += dtime * m_acceleration;
287 if((m_prop.automatic_face_movement_dir) &&
288 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
290 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
291 + m_prop.automatic_face_movement_dir_offset;
292 float max_rotation_delta =
293 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
295 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
296 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
298 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
306 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
309 if(send_recommended == false)
314 // TODO: force send when acceleration changes enough?
315 float minchange = 0.2*BS;
316 if(m_last_sent_position_timer > 1.0){
318 } else if(m_last_sent_position_timer > 0.2){
321 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
322 move_d += m_last_sent_move_precision;
323 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
324 if(move_d > minchange || vel_d > minchange ||
325 fabs(m_yaw - m_last_sent_yaw) > 1.0){
326 sendPosition(true, false);
330 if(m_armor_groups_sent == false){
331 m_armor_groups_sent = true;
332 std::string str = gob_cmd_update_armor_groups(
334 // create message and add to list
335 ActiveObjectMessage aom(getId(), true, str);
336 m_messages_out.push(aom);
339 if(m_animation_sent == false){
340 m_animation_sent = true;
341 std::string str = gob_cmd_update_animation(
342 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
343 // create message and add to list
344 ActiveObjectMessage aom(getId(), true, str);
345 m_messages_out.push(aom);
348 if(m_bone_position_sent == false){
349 m_bone_position_sent = true;
350 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
351 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
352 std::string str = gob_cmd_update_bone_position((*ii).first,
353 (*ii).second.X, (*ii).second.Y);
354 // create message and add to list
355 ActiveObjectMessage aom(getId(), true, str);
356 m_messages_out.push(aom);
360 if(m_attachment_sent == false){
361 m_attachment_sent = true;
362 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
363 // create message and add to list
364 ActiveObjectMessage aom(getId(), true, str);
365 m_messages_out.push(aom);
369 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
371 std::ostringstream os(std::ios::binary);
373 if(protocol_version >= 14)
375 writeU8(os, 1); // version
376 os<<serializeString(""); // name
377 writeU8(os, 0); // is_player
378 writeS16(os, getId()); //id
379 writeV3F1000(os, m_base_position);
380 writeF1000(os, m_yaw);
383 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
384 os<<serializeLongString(getPropertyPacket()); // message 1
385 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
386 os<<serializeLongString(gob_cmd_update_animation(
387 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
388 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
389 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
390 os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
391 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
393 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
397 writeU8(os, 0); // version
398 os<<serializeString(""); // name
399 writeU8(os, 0); // is_player
400 writeV3F1000(os, m_base_position);
401 writeF1000(os, m_yaw);
403 writeU8(os, 2); // number of messages stuffed in here
404 os<<serializeLongString(getPropertyPacket()); // message 1
405 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
412 std::string LuaEntitySAO::getStaticData()
414 verbosestream<<FUNCTION_NAME<<std::endl;
415 std::ostringstream os(std::ios::binary);
419 os<<serializeString(m_init_name);
422 std::string state = m_env->getScriptIface()->
423 luaentity_GetStaticdata(m_id);
424 os<<serializeLongString(state);
426 os<<serializeLongString(m_init_state);
431 writeV3F1000(os, m_velocity);
433 writeF1000(os, m_yaw);
437 int LuaEntitySAO::punch(v3f dir,
438 const ToolCapabilities *toolcap,
439 ServerActiveObject *puncher,
440 float time_from_last_punch)
443 // Delete unknown LuaEntities when punched
448 // It's best that attachments cannot be punched
452 ItemStack *punchitem = NULL;
453 ItemStack punchitem_static;
455 punchitem_static = puncher->getWieldedItem();
456 punchitem = &punchitem_static;
459 PunchDamageResult result = getPunchDamage(
463 time_from_last_punch);
465 if (result.did_punch) {
466 setHP(getHP() - result.damage);
468 if (result.damage > 0) {
469 std::string punchername = puncher ? puncher->getDescription() : "nil";
471 actionstream << getDescription() << " punched by "
472 << punchername << ", damage " << result.damage
473 << " hp, health now " << getHP() << " hp" << std::endl;
476 std::string str = gob_cmd_punched(result.damage, getHP());
477 // create message and add to list
478 ActiveObjectMessage aom(getId(), true, str);
479 m_messages_out.push(aom);
485 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
486 time_from_last_punch, toolcap, dir);
491 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
495 // It's best that attachments cannot be clicked
498 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
501 void LuaEntitySAO::setPos(v3f pos)
505 m_base_position = pos;
506 sendPosition(false, true);
509 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
513 m_base_position = pos;
515 sendPosition(true, true);
518 float LuaEntitySAO::getMinimumSavedMovement()
523 std::string LuaEntitySAO::getDescription()
525 std::ostringstream os(std::ios::binary);
526 os<<"LuaEntitySAO at (";
527 os<<(m_base_position.X/BS)<<",";
528 os<<(m_base_position.Y/BS)<<",";
529 os<<(m_base_position.Z/BS);
534 void LuaEntitySAO::setHP(s16 hp)
540 s16 LuaEntitySAO::getHP() const
545 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
547 m_armor_groups = armor_groups;
548 m_armor_groups_sent = false;
551 ItemGroupList LuaEntitySAO::getArmorGroups()
553 return m_armor_groups;
556 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
558 m_animation_range = frame_range;
559 m_animation_speed = frame_speed;
560 m_animation_blend = frame_blend;
561 m_animation_loop = frame_loop;
562 m_animation_sent = false;
565 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
567 *frame_range = m_animation_range;
568 *frame_speed = m_animation_speed;
569 *frame_blend = m_animation_blend;
570 *frame_loop = m_animation_loop;
573 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
575 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
576 m_bone_position_sent = false;
579 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
581 *position = m_bone_position[bone].X;
582 *rotation = m_bone_position[bone].Y;
585 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
587 // Attachments need to be handled on both the server and client.
588 // If we just attach on the server, we can only copy the position of the parent. Attachments
589 // are still sent to clients at an interval so players might see them lagging, plus we can't
590 // read and attach to skeletal bones.
591 // If we just attach on the client, the server still sees the child at its original location.
592 // This breaks some things so we also give the server the most accurate representation
593 // even if players only see the client changes.
595 m_attachment_parent_id = parent_id;
596 m_attachment_bone = bone;
597 m_attachment_position = position;
598 m_attachment_rotation = rotation;
599 m_attachment_sent = false;
602 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
605 *parent_id = m_attachment_parent_id;
606 *bone = m_attachment_bone;
607 *position = m_attachment_position;
608 *rotation = m_attachment_rotation;
611 void LuaEntitySAO::addAttachmentChild(int child_id)
613 m_attachment_child_ids.insert(child_id);
616 void LuaEntitySAO::removeAttachmentChild(int child_id)
618 m_attachment_child_ids.erase(child_id);
621 std::set<int> LuaEntitySAO::getAttachmentChildIds()
623 return m_attachment_child_ids;
626 ObjectProperties* LuaEntitySAO::accessObjectProperties()
631 void LuaEntitySAO::notifyObjectPropertiesModified()
633 m_properties_sent = false;
636 void LuaEntitySAO::setVelocity(v3f velocity)
638 m_velocity = velocity;
641 v3f LuaEntitySAO::getVelocity()
646 void LuaEntitySAO::setAcceleration(v3f acceleration)
648 m_acceleration = acceleration;
651 v3f LuaEntitySAO::getAcceleration()
653 return m_acceleration;
656 void LuaEntitySAO::setYaw(float yaw)
661 float LuaEntitySAO::getYaw()
666 void LuaEntitySAO::setTextureMod(const std::string &mod)
668 std::string str = gob_cmd_set_texture_mod(mod);
669 // create message and add to list
670 ActiveObjectMessage aom(getId(), true, str);
671 m_messages_out.push(aom);
674 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
675 bool select_horiz_by_yawpitch)
677 std::string str = gob_cmd_set_sprite(
681 select_horiz_by_yawpitch
683 // create message and add to list
684 ActiveObjectMessage aom(getId(), true, str);
685 m_messages_out.push(aom);
688 std::string LuaEntitySAO::getName()
693 std::string LuaEntitySAO::getPropertyPacket()
695 return gob_cmd_set_properties(m_prop);
698 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
700 // If the object is attached client-side, don't waste bandwidth sending its position to clients
704 m_last_sent_move_precision = m_base_position.getDistanceFrom(
705 m_last_sent_position);
706 m_last_sent_position_timer = 0;
707 m_last_sent_yaw = m_yaw;
708 m_last_sent_position = m_base_position;
709 m_last_sent_velocity = m_velocity;
710 //m_last_sent_acceleration = m_acceleration;
712 float update_interval = m_env->getSendRecommendedInterval();
714 std::string str = gob_cmd_update_position(
723 // create message and add to list
724 ActiveObjectMessage aom(getId(), false, str);
725 m_messages_out.push(aom);
728 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
731 //update collision box
732 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
733 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
735 toset->MinEdge += m_base_position;
736 toset->MaxEdge += m_base_position;
744 bool LuaEntitySAO::collideWithObjects(){
745 return m_prop.collideWithObjects;
752 // No prototype, PlayerSAO does not need to be deserialized
754 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
755 const std::set<std::string> &privs, bool is_singleplayer):
756 ServerActiveObject(env_, v3f(0,0,0)),
761 m_last_good_position(0,0,0),
762 m_time_from_last_punch(0),
763 m_nocheat_dig_pos(32767, 32767, 32767),
764 m_nocheat_dig_time(0),
766 m_position_not_sent(false),
767 m_armor_groups_sent(false),
768 m_properties_sent(true),
770 m_is_singleplayer(is_singleplayer),
771 m_animation_speed(0),
772 m_animation_blend(0),
773 m_animation_loop(true),
774 m_animation_sent(false),
775 m_bone_position_sent(false),
776 m_attachment_parent_id(0),
777 m_attachment_sent(false),
779 m_physics_override_speed(1),
780 m_physics_override_jump(1),
781 m_physics_override_gravity(1),
782 m_physics_override_sneak(true),
783 m_physics_override_sneak_glitch(true),
784 m_physics_override_sent(false)
786 assert(m_player); // pre-condition
787 assert(m_peer_id != 0); // pre-condition
788 setBasePosition(m_player->getPosition());
789 m_inventory = &m_player->inventory;
790 m_armor_groups["fleshy"] = 100;
792 m_prop.hp_max = PLAYER_MAX_HP;
793 m_prop.physical = false;
795 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
796 // start of default appearance, this should be overwritten by LUA
797 m_prop.visual = "upright_sprite";
798 m_prop.visual_size = v2f(1, 2);
799 m_prop.textures.clear();
800 m_prop.textures.push_back("player.png");
801 m_prop.textures.push_back("player_back.png");
802 m_prop.colors.clear();
803 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
804 m_prop.spritediv = v2s16(1,1);
805 // end of default appearance
806 m_prop.is_visible = true;
807 m_prop.makes_footstep_sound = true;
810 PlayerSAO::~PlayerSAO()
812 if(m_inventory != &m_player->inventory)
817 std::string PlayerSAO::getDescription()
819 return std::string("player ") + m_player->getName();
822 // Called after id has been set and has been inserted in environment
823 void PlayerSAO::addedToEnvironment(u32 dtime_s)
825 ServerActiveObject::addedToEnvironment(dtime_s);
826 ServerActiveObject::setBasePosition(m_player->getPosition());
827 m_player->setPlayerSAO(this);
828 m_player->peer_id = m_peer_id;
829 m_last_good_position = m_player->getPosition();
832 // Called before removing from environment
833 void PlayerSAO::removingFromEnvironment()
835 ServerActiveObject::removingFromEnvironment();
836 if(m_player->getPlayerSAO() == this)
838 m_player->setPlayerSAO(NULL);
839 m_player->peer_id = 0;
840 m_env->savePlayer((RemotePlayer*)m_player);
841 m_env->removePlayer(m_player);
845 bool PlayerSAO::isStaticAllowed() const
850 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
852 std::ostringstream os(std::ios::binary);
854 if(protocol_version >= 15)
856 writeU8(os, 1); // version
857 os<<serializeString(m_player->getName()); // name
858 writeU8(os, 1); // is_player
859 writeS16(os, getId()); //id
860 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
861 writeF1000(os, m_player->getYaw());
862 writeS16(os, getHP());
864 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
865 os<<serializeLongString(getPropertyPacket()); // message 1
866 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
867 os<<serializeLongString(gob_cmd_update_animation(
868 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
869 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
870 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
871 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
873 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
874 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
875 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
876 m_physics_override_sneak_glitch)); // 5
877 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
881 writeU8(os, 0); // version
882 os<<serializeString(m_player->getName()); // name
883 writeU8(os, 1); // is_player
884 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
885 writeF1000(os, m_player->getYaw());
886 writeS16(os, getHP());
887 writeU8(os, 2); // number of messages stuffed in here
888 os<<serializeLongString(getPropertyPacket()); // message 1
889 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
896 std::string PlayerSAO::getStaticData()
898 FATAL_ERROR("Deprecated function (?)");
902 bool PlayerSAO::isAttached()
904 if(!m_attachment_parent_id)
906 // Check if the parent still exists
907 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
913 void PlayerSAO::step(float dtime, bool send_recommended)
915 if(!m_properties_sent)
917 m_properties_sent = true;
918 std::string str = getPropertyPacket();
919 // create message and add to list
920 ActiveObjectMessage aom(getId(), true, str);
921 m_messages_out.push(aom);
924 // If attached, check that our parent is still there. If it isn't, detach.
925 if(m_attachment_parent_id && !isAttached())
927 m_attachment_parent_id = 0;
928 m_attachment_bone = "";
929 m_attachment_position = v3f(0,0,0);
930 m_attachment_rotation = v3f(0,0,0);
931 m_player->setPosition(m_last_good_position);
932 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
935 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
937 // Set lag pool maximums based on estimated lag
938 const float LAG_POOL_MIN = 5.0;
939 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
940 if(lag_pool_max < LAG_POOL_MIN)
941 lag_pool_max = LAG_POOL_MIN;
942 m_dig_pool.setMax(lag_pool_max);
943 m_move_pool.setMax(lag_pool_max);
945 // Increment cheat prevention timers
946 m_dig_pool.add(dtime);
947 m_move_pool.add(dtime);
948 m_time_from_last_punch += dtime;
949 m_nocheat_dig_time += dtime;
951 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
952 // If the object gets detached this comes into effect automatically from the last known origin
955 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
956 m_last_good_position = pos;
957 m_player->setPosition(pos);
960 if(send_recommended == false)
963 // If the object is attached client-side, don't waste bandwidth sending its position to clients
964 if(m_position_not_sent && !isAttached())
966 m_position_not_sent = false;
967 float update_interval = m_env->getSendRecommendedInterval();
969 if(isAttached()) // Just in case we ever do send attachment position too
970 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
972 pos = m_player->getPosition() + v3f(0,BS*1,0);
973 std::string str = gob_cmd_update_position(
982 // create message and add to list
983 ActiveObjectMessage aom(getId(), false, str);
984 m_messages_out.push(aom);
987 if(m_armor_groups_sent == false) {
988 m_armor_groups_sent = true;
989 std::string str = gob_cmd_update_armor_groups(
991 // create message and add to list
992 ActiveObjectMessage aom(getId(), true, str);
993 m_messages_out.push(aom);
996 if(m_physics_override_sent == false){
997 m_physics_override_sent = true;
998 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
999 m_physics_override_jump, m_physics_override_gravity,
1000 m_physics_override_sneak, m_physics_override_sneak_glitch);
1001 // create message and add to list
1002 ActiveObjectMessage aom(getId(), true, str);
1003 m_messages_out.push(aom);
1006 if(m_animation_sent == false){
1007 m_animation_sent = true;
1008 std::string str = gob_cmd_update_animation(
1009 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1010 // create message and add to list
1011 ActiveObjectMessage aom(getId(), true, str);
1012 m_messages_out.push(aom);
1015 if (!m_bone_position_sent) {
1016 m_bone_position_sent = true;
1017 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1018 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1019 std::string str = gob_cmd_update_bone_position((*ii).first,
1020 (*ii).second.X, (*ii).second.Y);
1021 // create message and add to list
1022 ActiveObjectMessage aom(getId(), true, str);
1023 m_messages_out.push(aom);
1027 if (!m_attachment_sent){
1028 m_attachment_sent = true;
1029 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1030 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1031 // create message and add to list
1032 ActiveObjectMessage aom(getId(), true, str);
1033 m_messages_out.push(aom);
1037 void PlayerSAO::setBasePosition(const v3f &position)
1039 // This needs to be ran for attachments too
1040 ServerActiveObject::setBasePosition(position);
1041 m_position_not_sent = true;
1044 void PlayerSAO::setPos(v3f pos)
1048 m_player->setPosition(pos);
1049 // Movement caused by this command is always valid
1050 m_last_good_position = pos;
1051 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1054 void PlayerSAO::moveTo(v3f pos, bool continuous)
1058 m_player->setPosition(pos);
1059 // Movement caused by this command is always valid
1060 m_last_good_position = pos;
1061 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1064 void PlayerSAO::setYaw(float yaw)
1066 m_player->setYaw(yaw);
1067 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1070 void PlayerSAO::setPitch(float pitch)
1072 m_player->setPitch(pitch);
1073 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1076 int PlayerSAO::punch(v3f dir,
1077 const ToolCapabilities *toolcap,
1078 ServerActiveObject *puncher,
1079 float time_from_last_punch)
1081 // It's best that attachments cannot be punched
1088 // No effect if PvP disabled
1089 if (g_settings->getBool("enable_pvp") == false) {
1090 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1091 std::string str = gob_cmd_punched(0, getHP());
1092 // create message and add to list
1093 ActiveObjectMessage aom(getId(), true, str);
1094 m_messages_out.push(aom);
1099 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1100 time_from_last_punch);
1102 std::string punchername = "nil";
1105 punchername = puncher->getDescription();
1107 PlayerSAO *playersao = m_player->getPlayerSAO();
1109 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1110 puncher, time_from_last_punch, toolcap, dir,
1113 if (!damage_handled) {
1114 setHP(getHP() - hitparams.hp);
1115 } else { // override client prediction
1116 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1117 std::string str = gob_cmd_punched(0, getHP());
1118 // create message and add to list
1119 ActiveObjectMessage aom(getId(), true, str);
1120 m_messages_out.push(aom);
1125 actionstream << "Player " << m_player->getName() << " punched by "
1127 if (!damage_handled) {
1128 actionstream << ", damage " << hitparams.hp << " HP";
1130 actionstream << ", damage handled by lua";
1132 actionstream << std::endl;
1134 return hitparams.wear;
1137 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1141 s16 PlayerSAO::getHP() const
1143 return m_player->hp;
1146 s16 PlayerSAO::readDamage()
1148 s16 damage = m_damage;
1153 void PlayerSAO::setHP(s16 hp)
1155 s16 oldhp = m_player->hp;
1157 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1161 hp = oldhp + hp_change;
1165 else if (hp > PLAYER_MAX_HP)
1168 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1175 m_damage += (oldhp - hp);
1177 // Update properties on death
1178 if ((hp == 0) != (oldhp == 0))
1179 m_properties_sent = false;
1182 u16 PlayerSAO::getBreath() const
1184 return m_player->getBreath();
1187 void PlayerSAO::setBreath(u16 breath)
1189 m_player->setBreath(breath);
1192 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1194 m_armor_groups = armor_groups;
1195 m_armor_groups_sent = false;
1198 ItemGroupList PlayerSAO::getArmorGroups()
1200 return m_armor_groups;
1203 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1205 // store these so they can be updated to clients
1206 m_animation_range = frame_range;
1207 m_animation_speed = frame_speed;
1208 m_animation_blend = frame_blend;
1209 m_animation_loop = frame_loop;
1210 m_animation_sent = false;
1213 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1215 *frame_range = m_animation_range;
1216 *frame_speed = m_animation_speed;
1217 *frame_blend = m_animation_blend;
1218 *frame_loop = m_animation_loop;
1221 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1223 // store these so they can be updated to clients
1224 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1225 m_bone_position_sent = false;
1228 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1230 *position = m_bone_position[bone].X;
1231 *rotation = m_bone_position[bone].Y;
1234 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1236 // Attachments need to be handled on both the server and client.
1237 // If we just attach on the server, we can only copy the position of the parent. Attachments
1238 // are still sent to clients at an interval so players might see them lagging, plus we can't
1239 // read and attach to skeletal bones.
1240 // If we just attach on the client, the server still sees the child at its original location.
1241 // This breaks some things so we also give the server the most accurate representation
1242 // even if players only see the client changes.
1244 m_attachment_parent_id = parent_id;
1245 m_attachment_bone = bone;
1246 m_attachment_position = position;
1247 m_attachment_rotation = rotation;
1248 m_attachment_sent = false;
1251 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1254 *parent_id = m_attachment_parent_id;
1255 *bone = m_attachment_bone;
1256 *position = m_attachment_position;
1257 *rotation = m_attachment_rotation;
1260 void PlayerSAO::addAttachmentChild(int child_id)
1262 m_attachment_child_ids.insert(child_id);
1265 void PlayerSAO::removeAttachmentChild(int child_id)
1267 m_attachment_child_ids.erase(child_id);
1270 std::set<int> PlayerSAO::getAttachmentChildIds()
1272 return m_attachment_child_ids;
1275 ObjectProperties* PlayerSAO::accessObjectProperties()
1280 void PlayerSAO::notifyObjectPropertiesModified()
1282 m_properties_sent = false;
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 if (isAttached() || m_is_singleplayer ||
1338 g_settings->getBool("disable_anticheat")) {
1339 m_last_good_position = m_player->getPosition();
1343 bool cheated = false;
1345 Check player movements
1347 NOTE: Actually the server should handle player physics like the
1348 client does and compare player's position to what is calculated
1349 on our side. This is required when eg. players fly due to an
1350 explosion. Altough a node-based alternative might be possible
1351 too, and much more lightweight.
1354 float player_max_speed = 0;
1356 if (m_privs.count("fast") != 0) {
1358 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1361 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1363 // Tolerance. The lag pool does this a bit.
1364 //player_max_speed *= 2.5;
1366 v3f diff = (m_player->getPosition() - m_last_good_position);
1367 float d_vert = diff.Y;
1369 float d_horiz = diff.getLength();
1370 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; // Moving upwards
1375 if (m_move_pool.grab(required_time)) {
1376 m_last_good_position = m_player->getPosition();
1378 actionstream << "Player " << m_player->getName()
1379 << " moved too fast; resetting position"
1381 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(){