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_, RemotePlayer *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) {
837 m_player->setPlayerSAO(NULL);
838 m_player->peer_id = 0;
839 m_env->savePlayer(m_player);
840 m_env->removePlayer(m_player);
844 bool PlayerSAO::isStaticAllowed() const
849 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
851 std::ostringstream os(std::ios::binary);
853 if(protocol_version >= 15)
855 writeU8(os, 1); // version
856 os<<serializeString(m_player->getName()); // name
857 writeU8(os, 1); // is_player
858 writeS16(os, getId()); //id
859 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
860 writeF1000(os, m_player->getYaw());
861 writeS16(os, getHP());
863 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
864 os<<serializeLongString(getPropertyPacket()); // message 1
865 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
866 os<<serializeLongString(gob_cmd_update_animation(
867 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
868 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
869 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
870 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
872 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
873 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
874 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
875 m_physics_override_sneak_glitch)); // 5
876 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
880 writeU8(os, 0); // version
881 os<<serializeString(m_player->getName()); // name
882 writeU8(os, 1); // is_player
883 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
884 writeF1000(os, m_player->getYaw());
885 writeS16(os, getHP());
886 writeU8(os, 2); // number of messages stuffed in here
887 os<<serializeLongString(getPropertyPacket()); // message 1
888 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
895 std::string PlayerSAO::getStaticData()
897 FATAL_ERROR("Deprecated function (?)");
901 bool PlayerSAO::isAttached()
903 if(!m_attachment_parent_id)
905 // Check if the parent still exists
906 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
912 void PlayerSAO::step(float dtime, bool send_recommended)
914 if(!m_properties_sent)
916 m_properties_sent = true;
917 std::string str = getPropertyPacket();
918 // create message and add to list
919 ActiveObjectMessage aom(getId(), true, str);
920 m_messages_out.push(aom);
923 // If attached, check that our parent is still there. If it isn't, detach.
924 if(m_attachment_parent_id && !isAttached())
926 m_attachment_parent_id = 0;
927 m_attachment_bone = "";
928 m_attachment_position = v3f(0,0,0);
929 m_attachment_rotation = v3f(0,0,0);
930 m_player->setPosition(m_last_good_position);
931 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
934 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
936 // Set lag pool maximums based on estimated lag
937 const float LAG_POOL_MIN = 5.0;
938 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
939 if(lag_pool_max < LAG_POOL_MIN)
940 lag_pool_max = LAG_POOL_MIN;
941 m_dig_pool.setMax(lag_pool_max);
942 m_move_pool.setMax(lag_pool_max);
944 // Increment cheat prevention timers
945 m_dig_pool.add(dtime);
946 m_move_pool.add(dtime);
947 m_time_from_last_punch += dtime;
948 m_nocheat_dig_time += dtime;
950 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
951 // If the object gets detached this comes into effect automatically from the last known origin
954 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
955 m_last_good_position = pos;
956 m_player->setPosition(pos);
959 if(send_recommended == false)
962 // If the object is attached client-side, don't waste bandwidth sending its position to clients
963 if(m_position_not_sent && !isAttached())
965 m_position_not_sent = false;
966 float update_interval = m_env->getSendRecommendedInterval();
968 if(isAttached()) // Just in case we ever do send attachment position too
969 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
971 pos = m_player->getPosition() + v3f(0,BS*1,0);
972 std::string str = gob_cmd_update_position(
981 // create message and add to list
982 ActiveObjectMessage aom(getId(), false, str);
983 m_messages_out.push(aom);
986 if(m_armor_groups_sent == false) {
987 m_armor_groups_sent = true;
988 std::string str = gob_cmd_update_armor_groups(
990 // create message and add to list
991 ActiveObjectMessage aom(getId(), true, str);
992 m_messages_out.push(aom);
995 if(m_physics_override_sent == false){
996 m_physics_override_sent = true;
997 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
998 m_physics_override_jump, m_physics_override_gravity,
999 m_physics_override_sneak, m_physics_override_sneak_glitch);
1000 // create message and add to list
1001 ActiveObjectMessage aom(getId(), true, str);
1002 m_messages_out.push(aom);
1005 if(m_animation_sent == false){
1006 m_animation_sent = true;
1007 std::string str = gob_cmd_update_animation(
1008 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1009 // create message and add to list
1010 ActiveObjectMessage aom(getId(), true, str);
1011 m_messages_out.push(aom);
1014 if (!m_bone_position_sent) {
1015 m_bone_position_sent = true;
1016 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1017 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1018 std::string str = gob_cmd_update_bone_position((*ii).first,
1019 (*ii).second.X, (*ii).second.Y);
1020 // create message and add to list
1021 ActiveObjectMessage aom(getId(), true, str);
1022 m_messages_out.push(aom);
1026 if (!m_attachment_sent){
1027 m_attachment_sent = true;
1028 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1029 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1030 // create message and add to list
1031 ActiveObjectMessage aom(getId(), true, str);
1032 m_messages_out.push(aom);
1036 void PlayerSAO::setBasePosition(const v3f &position)
1038 // This needs to be ran for attachments too
1039 ServerActiveObject::setBasePosition(position);
1040 m_position_not_sent = true;
1043 void PlayerSAO::setPos(v3f pos)
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::moveTo(v3f pos, bool continuous)
1057 m_player->setPosition(pos);
1058 // Movement caused by this command is always valid
1059 m_last_good_position = pos;
1060 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1063 void PlayerSAO::setYaw(float yaw)
1065 m_player->setYaw(yaw);
1066 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1069 void PlayerSAO::setPitch(float pitch)
1071 m_player->setPitch(pitch);
1072 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1075 int PlayerSAO::punch(v3f dir,
1076 const ToolCapabilities *toolcap,
1077 ServerActiveObject *puncher,
1078 float time_from_last_punch)
1080 // It's best that attachments cannot be punched
1087 // No effect if PvP disabled
1088 if (g_settings->getBool("enable_pvp") == false) {
1089 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1090 std::string str = gob_cmd_punched(0, getHP());
1091 // create message and add to list
1092 ActiveObjectMessage aom(getId(), true, str);
1093 m_messages_out.push(aom);
1098 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1099 time_from_last_punch);
1101 std::string punchername = "nil";
1104 punchername = puncher->getDescription();
1106 PlayerSAO *playersao = m_player->getPlayerSAO();
1108 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1109 puncher, time_from_last_punch, toolcap, dir,
1112 if (!damage_handled) {
1113 setHP(getHP() - hitparams.hp);
1114 } else { // override client prediction
1115 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1116 std::string str = gob_cmd_punched(0, getHP());
1117 // create message and add to list
1118 ActiveObjectMessage aom(getId(), true, str);
1119 m_messages_out.push(aom);
1124 actionstream << "Player " << m_player->getName() << " punched by "
1126 if (!damage_handled) {
1127 actionstream << ", damage " << hitparams.hp << " HP";
1129 actionstream << ", damage handled by lua";
1131 actionstream << std::endl;
1133 return hitparams.wear;
1136 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1140 s16 PlayerSAO::getHP() const
1142 return m_player->hp;
1145 s16 PlayerSAO::readDamage()
1147 s16 damage = m_damage;
1152 void PlayerSAO::setHP(s16 hp)
1154 s16 oldhp = m_player->hp;
1156 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1160 hp = oldhp + hp_change;
1164 else if (hp > PLAYER_MAX_HP)
1167 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1174 m_damage += (oldhp - hp);
1176 // Update properties on death
1177 if ((hp == 0) != (oldhp == 0))
1178 m_properties_sent = false;
1181 u16 PlayerSAO::getBreath() const
1183 return m_player->getBreath();
1186 void PlayerSAO::setBreath(u16 breath)
1188 m_player->setBreath(breath);
1191 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1193 m_armor_groups = armor_groups;
1194 m_armor_groups_sent = false;
1197 ItemGroupList PlayerSAO::getArmorGroups()
1199 return m_armor_groups;
1202 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1204 // store these so they can be updated to clients
1205 m_animation_range = frame_range;
1206 m_animation_speed = frame_speed;
1207 m_animation_blend = frame_blend;
1208 m_animation_loop = frame_loop;
1209 m_animation_sent = false;
1212 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1214 *frame_range = m_animation_range;
1215 *frame_speed = m_animation_speed;
1216 *frame_blend = m_animation_blend;
1217 *frame_loop = m_animation_loop;
1220 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1222 // store these so they can be updated to clients
1223 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1224 m_bone_position_sent = false;
1227 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1229 *position = m_bone_position[bone].X;
1230 *rotation = m_bone_position[bone].Y;
1233 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1235 // Attachments need to be handled on both the server and client.
1236 // If we just attach on the server, we can only copy the position of the parent. Attachments
1237 // are still sent to clients at an interval so players might see them lagging, plus we can't
1238 // read and attach to skeletal bones.
1239 // If we just attach on the client, the server still sees the child at its original location.
1240 // This breaks some things so we also give the server the most accurate representation
1241 // even if players only see the client changes.
1243 m_attachment_parent_id = parent_id;
1244 m_attachment_bone = bone;
1245 m_attachment_position = position;
1246 m_attachment_rotation = rotation;
1247 m_attachment_sent = false;
1250 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1253 *parent_id = m_attachment_parent_id;
1254 *bone = m_attachment_bone;
1255 *position = m_attachment_position;
1256 *rotation = m_attachment_rotation;
1259 void PlayerSAO::addAttachmentChild(int child_id)
1261 m_attachment_child_ids.insert(child_id);
1264 void PlayerSAO::removeAttachmentChild(int child_id)
1266 m_attachment_child_ids.erase(child_id);
1269 std::set<int> PlayerSAO::getAttachmentChildIds()
1271 return m_attachment_child_ids;
1274 ObjectProperties* PlayerSAO::accessObjectProperties()
1279 void PlayerSAO::notifyObjectPropertiesModified()
1281 m_properties_sent = false;
1284 Inventory* PlayerSAO::getInventory()
1288 const Inventory* PlayerSAO::getInventory() const
1293 InventoryLocation PlayerSAO::getInventoryLocation() const
1295 InventoryLocation loc;
1296 loc.setPlayer(m_player->getName());
1300 std::string PlayerSAO::getWieldList() const
1305 int PlayerSAO::getWieldIndex() const
1307 return m_wield_index;
1310 void PlayerSAO::setWieldIndex(int i)
1312 if(i != m_wield_index) {
1317 void PlayerSAO::disconnected()
1321 if(m_player->getPlayerSAO() == this)
1323 m_player->setPlayerSAO(NULL);
1324 m_player->peer_id = 0;
1328 std::string PlayerSAO::getPropertyPacket()
1330 m_prop.is_visible = (true);
1331 return gob_cmd_set_properties(m_prop);
1334 bool PlayerSAO::checkMovementCheat()
1336 if (isAttached() || m_is_singleplayer ||
1337 g_settings->getBool("disable_anticheat")) {
1338 m_last_good_position = m_player->getPosition();
1342 bool cheated = false;
1344 Check player movements
1346 NOTE: Actually the server should handle player physics like the
1347 client does and compare player's position to what is calculated
1348 on our side. This is required when eg. players fly due to an
1349 explosion. Altough a node-based alternative might be possible
1350 too, and much more lightweight.
1353 float player_max_speed = 0;
1355 if (m_privs.count("fast") != 0) {
1357 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1360 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1362 // Tolerance. The lag pool does this a bit.
1363 //player_max_speed *= 2.5;
1365 v3f diff = (m_player->getPosition() - m_last_good_position);
1366 float d_vert = diff.Y;
1368 float d_horiz = diff.getLength();
1369 float required_time = d_horiz / player_max_speed;
1371 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1372 required_time = d_vert / player_max_speed; // Moving upwards
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);
1386 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1387 //update collision box
1388 *toset = m_player->getCollisionbox();
1390 toset->MinEdge += m_base_position;
1391 toset->MaxEdge += m_base_position;
1396 bool PlayerSAO::collideWithObjects(){