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 "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
27 #include "remoteplayer.h"
29 #include "scripting_game.h"
30 #include "genericobject.h"
32 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38 class TestSAO : public ServerActiveObject
41 TestSAO(ServerEnvironment *env, v3f pos):
42 ServerActiveObject(env, pos),
46 ServerActiveObject::registerType(getType(), create);
48 ActiveObjectType getType() const
49 { return ACTIVEOBJECT_TYPE_TEST; }
51 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
52 const std::string &data)
54 return new TestSAO(env, pos);
57 void step(float dtime, bool send_recommended)
66 m_base_position.Y += dtime * BS * 2;
67 if(m_base_position.Y > 8*BS)
68 m_base_position.Y = 2*BS;
70 if(send_recommended == false)
80 data += itos(0); // 0 = position
82 data += itos(m_base_position.X);
84 data += itos(m_base_position.Y);
86 data += itos(m_base_position.Z);
88 ActiveObjectMessage aom(getId(), false, data);
89 m_messages_out.push(aom);
93 bool getCollisionBox(aabb3f *toset) const { return false; }
94 bool collideWithObjects() const { return false; }
101 // Prototype (registers item for deserialization)
102 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
108 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
109 ServerActiveObject(env, pos),
112 m_properties_sent(true),
113 m_armor_groups_sent(false),
114 m_animation_range(0,0),
115 m_animation_speed(0),
116 m_animation_blend(0),
117 m_animation_loop(true),
118 m_animation_sent(false),
119 m_bone_position_sent(false),
120 m_attachment_parent_id(0),
121 m_attachment_sent(false)
123 // Initialize something to armor groups
124 m_armor_groups["fleshy"] = 100;
127 bool UnitSAO::isAttached() const
129 if (!m_attachment_parent_id)
131 // Check if the parent still exists
132 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
138 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 m_armor_groups = armor_groups;
141 m_armor_groups_sent = false;
144 const ItemGroupList &UnitSAO::getArmorGroups()
146 return m_armor_groups;
149 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 // store these so they can be updated to clients
152 m_animation_range = frame_range;
153 m_animation_speed = frame_speed;
154 m_animation_blend = frame_blend;
155 m_animation_loop = frame_loop;
156 m_animation_sent = false;
159 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 *frame_range = m_animation_range;
162 *frame_speed = m_animation_speed;
163 *frame_blend = m_animation_blend;
164 *frame_loop = m_animation_loop;
167 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 // store these so they can be updated to clients
170 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
171 m_bone_position_sent = false;
174 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 *position = m_bone_position[bone].X;
177 *rotation = m_bone_position[bone].Y;
180 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 // Attachments need to be handled on both the server and client.
183 // If we just attach on the server, we can only copy the position of the parent. Attachments
184 // are still sent to clients at an interval so players might see them lagging, plus we can't
185 // read and attach to skeletal bones.
186 // If we just attach on the client, the server still sees the child at its original location.
187 // This breaks some things so we also give the server the most accurate representation
188 // even if players only see the client changes.
190 m_attachment_parent_id = parent_id;
191 m_attachment_bone = bone;
192 m_attachment_position = position;
193 m_attachment_rotation = rotation;
194 m_attachment_sent = false;
197 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
200 *parent_id = m_attachment_parent_id;
201 *bone = m_attachment_bone;
202 *position = m_attachment_position;
203 *rotation = m_attachment_rotation;
206 void UnitSAO::detachFromParent()
208 ServerActiveObject *parent = NULL;
209 if (m_attachment_parent_id)
210 parent = m_env->getActiveObject(m_attachment_parent_id);
211 setAttachment(NULL, "", v3f(0, 0, 0), v3f(0, 0, 0));
213 parent->removeAttachmentChild(m_id);
216 void UnitSAO::addAttachmentChild(int child_id)
218 m_attachment_child_ids.insert(child_id);
221 void UnitSAO::removeAttachmentChild(int child_id)
223 m_attachment_child_ids.erase(child_id);
226 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
228 return m_attachment_child_ids;
231 ObjectProperties* UnitSAO::accessObjectProperties()
236 void UnitSAO::notifyObjectPropertiesModified()
238 m_properties_sent = false;
245 // Prototype (registers item for deserialization)
246 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
248 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
249 const std::string &name, const std::string &state):
255 m_acceleration(0,0,0),
257 m_last_sent_position(0,0,0),
258 m_last_sent_velocity(0,0,0),
259 m_last_sent_position_timer(0),
260 m_last_sent_move_precision(0),
261 m_current_texture_modifier("")
263 // Only register type if no environment supplied
265 ServerActiveObject::registerType(getType(), create);
270 LuaEntitySAO::~LuaEntitySAO()
273 m_env->getScriptIface()->luaentity_Remove(m_id);
276 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
277 it != m_attached_particle_spawners.end(); ++it) {
278 m_env->deleteParticleSpawner(*it, false);
282 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
284 ServerActiveObject::addedToEnvironment(dtime_s);
286 // Create entity from name
287 m_registered = m_env->getScriptIface()->
288 luaentity_Add(m_id, m_init_name.c_str());
292 m_env->getScriptIface()->
293 luaentity_GetProperties(m_id, &m_prop);
294 // Initialize HP from properties
295 m_hp = m_prop.hp_max;
296 // Activate entity, supplying serialized state
297 m_env->getScriptIface()->
298 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
300 m_prop.infotext = m_init_name;
304 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
305 const std::string &data)
313 std::istringstream is(data, std::ios::binary);
315 u8 version = readU8(is);
316 // check if version is supported
318 name = deSerializeString(is);
319 state = deSerializeLongString(is);
321 else if(version == 1){
322 name = deSerializeString(is);
323 state = deSerializeLongString(is);
325 velocity = readV3F1000(is);
330 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
331 <<state<<"\")"<<std::endl;
332 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
334 sao->m_velocity = velocity;
339 void LuaEntitySAO::step(float dtime, bool send_recommended)
341 if(!m_properties_sent)
343 m_properties_sent = true;
344 std::string str = getPropertyPacket();
345 // create message and add to list
346 ActiveObjectMessage aom(getId(), true, str);
347 m_messages_out.push(aom);
350 // If attached, check that our parent is still there. If it isn't, detach.
351 if(m_attachment_parent_id && !isAttached())
353 m_attachment_parent_id = 0;
354 m_attachment_bone = "";
355 m_attachment_position = v3f(0,0,0);
356 m_attachment_rotation = v3f(0,0,0);
357 sendPosition(false, true);
360 m_last_sent_position_timer += dtime;
362 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
363 // If the object gets detached this comes into effect automatically from the last known origin
366 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
367 m_base_position = pos;
368 m_velocity = v3f(0,0,0);
369 m_acceleration = v3f(0,0,0);
374 aabb3f box = m_prop.collisionbox;
377 collisionMoveResult moveresult;
378 f32 pos_max_d = BS*0.25; // Distance per iteration
379 v3f p_pos = m_base_position;
380 v3f p_velocity = m_velocity;
381 v3f p_acceleration = m_acceleration;
382 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
383 pos_max_d, box, m_prop.stepheight, dtime,
384 &p_pos, &p_velocity, p_acceleration,
385 this, m_prop.collideWithObjects);
388 m_base_position = p_pos;
389 m_velocity = p_velocity;
390 m_acceleration = p_acceleration;
392 m_base_position += dtime * m_velocity + 0.5 * dtime
393 * dtime * m_acceleration;
394 m_velocity += dtime * m_acceleration;
397 if((m_prop.automatic_face_movement_dir) &&
398 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
400 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
401 + m_prop.automatic_face_movement_dir_offset;
402 float max_rotation_delta =
403 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
405 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
406 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
408 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
416 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
419 if(send_recommended == false)
424 // TODO: force send when acceleration changes enough?
425 float minchange = 0.2*BS;
426 if(m_last_sent_position_timer > 1.0){
428 } else if(m_last_sent_position_timer > 0.2){
431 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
432 move_d += m_last_sent_move_precision;
433 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
434 if(move_d > minchange || vel_d > minchange ||
435 fabs(m_yaw - m_last_sent_yaw) > 1.0){
436 sendPosition(true, false);
440 if(m_armor_groups_sent == false){
441 m_armor_groups_sent = true;
442 std::string str = gob_cmd_update_armor_groups(
444 // create message and add to list
445 ActiveObjectMessage aom(getId(), true, str);
446 m_messages_out.push(aom);
449 if(m_animation_sent == false){
450 m_animation_sent = true;
451 std::string str = gob_cmd_update_animation(
452 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
453 // create message and add to list
454 ActiveObjectMessage aom(getId(), true, str);
455 m_messages_out.push(aom);
458 if(m_bone_position_sent == false){
459 m_bone_position_sent = true;
460 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
461 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
462 std::string str = gob_cmd_update_bone_position((*ii).first,
463 (*ii).second.X, (*ii).second.Y);
464 // create message and add to list
465 ActiveObjectMessage aom(getId(), true, str);
466 m_messages_out.push(aom);
470 if(m_attachment_sent == false){
471 m_attachment_sent = true;
472 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
473 // create message and add to list
474 ActiveObjectMessage aom(getId(), true, str);
475 m_messages_out.push(aom);
479 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
481 std::ostringstream os(std::ios::binary);
483 if(protocol_version >= 14)
485 writeU8(os, 1); // version
486 os<<serializeString(""); // name
487 writeU8(os, 0); // is_player
488 writeS16(os, getId()); //id
489 writeV3F1000(os, m_base_position);
490 writeF1000(os, m_yaw);
493 std::ostringstream msg_os(std::ios::binary);
494 msg_os << serializeLongString(getPropertyPacket()); // message 1
495 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
496 msg_os << serializeLongString(gob_cmd_update_animation(
497 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
498 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
499 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
500 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
501 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
503 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
504 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
505 int message_count = 4 + m_bone_position.size();
506 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
507 (ii != m_attachment_child_ids.end()); ++ii) {
508 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
510 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
511 obj->getClientInitializationData(protocol_version)));
515 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
518 writeU8(os, message_count);
519 os.write(msg_os.str().c_str(), msg_os.str().size());
523 writeU8(os, 0); // version
524 os<<serializeString(""); // name
525 writeU8(os, 0); // is_player
526 writeV3F1000(os, m_base_position);
527 writeF1000(os, m_yaw);
529 writeU8(os, 2); // number of messages stuffed in here
530 os<<serializeLongString(getPropertyPacket()); // message 1
531 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
538 void LuaEntitySAO::getStaticData(std::string *result) const
540 verbosestream<<FUNCTION_NAME<<std::endl;
541 std::ostringstream os(std::ios::binary);
545 os<<serializeString(m_init_name);
548 std::string state = m_env->getScriptIface()->
549 luaentity_GetStaticdata(m_id);
550 os<<serializeLongString(state);
552 os<<serializeLongString(m_init_state);
557 writeV3F1000(os, m_velocity);
559 writeF1000(os, m_yaw);
563 int LuaEntitySAO::punch(v3f dir,
564 const ToolCapabilities *toolcap,
565 ServerActiveObject *puncher,
566 float time_from_last_punch)
569 // Delete unknown LuaEntities when punched
574 // It's best that attachments cannot be punched
578 ItemStack *punchitem = NULL;
579 ItemStack punchitem_static;
581 punchitem_static = puncher->getWieldedItem();
582 punchitem = &punchitem_static;
585 PunchDamageResult result = getPunchDamage(
589 time_from_last_punch);
591 if (result.did_punch) {
592 setHP(getHP() - result.damage);
594 if (result.damage > 0) {
595 std::string punchername = puncher ? puncher->getDescription() : "nil";
597 actionstream << getDescription() << " punched by "
598 << punchername << ", damage " << result.damage
599 << " hp, health now " << getHP() << " hp" << std::endl;
602 std::string str = gob_cmd_punched(result.damage, getHP());
603 // create message and add to list
604 ActiveObjectMessage aom(getId(), true, str);
605 m_messages_out.push(aom);
611 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
612 time_from_last_punch, toolcap, dir);
617 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
621 // It's best that attachments cannot be clicked
624 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
627 void LuaEntitySAO::setPos(const v3f &pos)
631 m_base_position = pos;
632 sendPosition(false, true);
635 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
639 m_base_position = pos;
641 sendPosition(true, true);
644 float LuaEntitySAO::getMinimumSavedMovement()
649 std::string LuaEntitySAO::getDescription()
651 std::ostringstream os(std::ios::binary);
652 os<<"LuaEntitySAO at (";
653 os<<(m_base_position.X/BS)<<",";
654 os<<(m_base_position.Y/BS)<<",";
655 os<<(m_base_position.Z/BS);
660 void LuaEntitySAO::setHP(s16 hp)
666 s16 LuaEntitySAO::getHP() const
671 void LuaEntitySAO::setVelocity(v3f velocity)
673 m_velocity = velocity;
676 v3f LuaEntitySAO::getVelocity()
681 void LuaEntitySAO::setAcceleration(v3f acceleration)
683 m_acceleration = acceleration;
686 v3f LuaEntitySAO::getAcceleration()
688 return m_acceleration;
691 void LuaEntitySAO::setTextureMod(const std::string &mod)
693 std::string str = gob_cmd_set_texture_mod(mod);
694 m_current_texture_modifier = mod;
695 // create message and add to list
696 ActiveObjectMessage aom(getId(), true, str);
697 m_messages_out.push(aom);
700 std::string LuaEntitySAO::getTextureMod() const
702 return m_current_texture_modifier;
705 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
706 bool select_horiz_by_yawpitch)
708 std::string str = gob_cmd_set_sprite(
712 select_horiz_by_yawpitch
714 // create message and add to list
715 ActiveObjectMessage aom(getId(), true, str);
716 m_messages_out.push(aom);
719 std::string LuaEntitySAO::getName()
724 std::string LuaEntitySAO::getPropertyPacket()
726 return gob_cmd_set_properties(m_prop);
729 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
731 // If the object is attached client-side, don't waste bandwidth sending its position to clients
735 m_last_sent_move_precision = m_base_position.getDistanceFrom(
736 m_last_sent_position);
737 m_last_sent_position_timer = 0;
738 m_last_sent_yaw = m_yaw;
739 m_last_sent_position = m_base_position;
740 m_last_sent_velocity = m_velocity;
741 //m_last_sent_acceleration = m_acceleration;
743 float update_interval = m_env->getSendRecommendedInterval();
745 std::string str = gob_cmd_update_position(
754 // create message and add to list
755 ActiveObjectMessage aom(getId(), false, str);
756 m_messages_out.push(aom);
759 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
763 //update collision box
764 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
765 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
767 toset->MinEdge += m_base_position;
768 toset->MaxEdge += m_base_position;
776 bool LuaEntitySAO::collideWithObjects() const
778 return m_prop.collideWithObjects;
785 // No prototype, PlayerSAO does not need to be deserialized
787 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
788 UnitSAO(env_, v3f(0,0,0)),
793 m_last_good_position(0,0,0),
794 m_time_from_last_punch(0),
795 m_nocheat_dig_pos(32767, 32767, 32767),
796 m_nocheat_dig_time(0),
798 m_position_not_sent(false),
799 m_is_singleplayer(is_singleplayer),
800 m_breath(PLAYER_MAX_BREATH),
805 m_physics_override_speed(1),
806 m_physics_override_jump(1),
807 m_physics_override_gravity(1),
808 m_physics_override_sneak(true),
809 m_physics_override_sneak_glitch(true),
810 m_physics_override_sent(false)
812 assert(m_peer_id != 0); // pre-condition
814 m_prop.hp_max = PLAYER_MAX_HP;
815 m_prop.physical = false;
817 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
818 // start of default appearance, this should be overwritten by LUA
819 m_prop.visual = "upright_sprite";
820 m_prop.visual_size = v2f(1, 2);
821 m_prop.textures.clear();
822 m_prop.textures.push_back("player.png");
823 m_prop.textures.push_back("player_back.png");
824 m_prop.colors.clear();
825 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
826 m_prop.spritediv = v2s16(1,1);
827 // end of default appearance
828 m_prop.is_visible = true;
829 m_prop.makes_footstep_sound = true;
830 m_hp = PLAYER_MAX_HP;
833 PlayerSAO::~PlayerSAO()
835 if(m_inventory != &m_player->inventory)
839 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
844 m_inventory = &m_player->inventory;
847 v3f PlayerSAO::getEyeOffset() const
849 return v3f(0, BS * 1.625f, 0);
852 std::string PlayerSAO::getDescription()
854 return std::string("player ") + m_player->getName();
857 // Called after id has been set and has been inserted in environment
858 void PlayerSAO::addedToEnvironment(u32 dtime_s)
860 ServerActiveObject::addedToEnvironment(dtime_s);
861 ServerActiveObject::setBasePosition(m_base_position);
862 m_player->setPlayerSAO(this);
863 m_player->peer_id = m_peer_id;
864 m_last_good_position = m_base_position;
867 // Called before removing from environment
868 void PlayerSAO::removingFromEnvironment()
870 ServerActiveObject::removingFromEnvironment();
871 if (m_player->getPlayerSAO() == this) {
872 unlinkPlayerSessionAndSave();
873 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
874 it != m_attached_particle_spawners.end(); ++it) {
875 m_env->deleteParticleSpawner(*it, false);
880 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
882 std::ostringstream os(std::ios::binary);
884 if(protocol_version >= 15)
886 writeU8(os, 1); // version
887 os<<serializeString(m_player->getName()); // name
888 writeU8(os, 1); // is_player
889 writeS16(os, getId()); //id
890 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
891 writeF1000(os, m_yaw);
892 writeS16(os, getHP());
894 std::ostringstream msg_os(std::ios::binary);
895 msg_os << serializeLongString(getPropertyPacket()); // message 1
896 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
897 msg_os << serializeLongString(gob_cmd_update_animation(
898 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
899 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
900 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
901 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
902 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
904 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
905 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
906 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
907 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
908 m_physics_override_sneak_glitch)); // 5
909 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
910 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
911 int message_count = 6 + m_bone_position.size();
912 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
913 ii != m_attachment_child_ids.end(); ++ii) {
914 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
916 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
917 obj->getClientInitializationData(protocol_version)));
921 writeU8(os, message_count);
922 os.write(msg_os.str().c_str(), msg_os.str().size());
926 writeU8(os, 0); // version
927 os<<serializeString(m_player->getName()); // name
928 writeU8(os, 1); // is_player
929 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
930 writeF1000(os, m_yaw);
931 writeS16(os, getHP());
932 writeU8(os, 2); // number of messages stuffed in here
933 os<<serializeLongString(getPropertyPacket()); // message 1
934 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
941 void PlayerSAO::getStaticData(std::string *result) const
943 FATAL_ERROR("Deprecated function");
946 void PlayerSAO::step(float dtime, bool send_recommended)
948 if (m_drowning_interval.step(dtime, 2.0)) {
950 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
951 MapNode n = m_env->getMap().getNodeNoEx(p);
952 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
953 // If node generates drown
954 if (c.drowning > 0) {
955 if (m_hp > 0 && m_breath > 0)
956 setBreath(m_breath - 1);
958 // No more breath, damage player
960 setHP(m_hp - c.drowning);
961 m_env->getGameDef()->SendPlayerHPOrDie(this);
966 if (m_breathing_interval.step(dtime, 0.5)) {
968 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
969 MapNode n = m_env->getMap().getNodeNoEx(p);
970 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
971 // If player is alive & no drowning, breath
972 if (m_hp > 0 && c.drowning == 0)
973 setBreath(m_breath + 1);
976 if (!m_properties_sent) {
977 m_properties_sent = true;
978 std::string str = getPropertyPacket();
979 // create message and add to list
980 ActiveObjectMessage aom(getId(), true, str);
981 m_messages_out.push(aom);
984 // If attached, check that our parent is still there. If it isn't, detach.
985 if(m_attachment_parent_id && !isAttached())
987 m_attachment_parent_id = 0;
988 m_attachment_bone = "";
989 m_attachment_position = v3f(0,0,0);
990 m_attachment_rotation = v3f(0,0,0);
991 setBasePosition(m_last_good_position);
992 m_env->getGameDef()->SendMovePlayer(m_peer_id);
995 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
997 // Set lag pool maximums based on estimated lag
998 const float LAG_POOL_MIN = 5.0;
999 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1000 if(lag_pool_max < LAG_POOL_MIN)
1001 lag_pool_max = LAG_POOL_MIN;
1002 m_dig_pool.setMax(lag_pool_max);
1003 m_move_pool.setMax(lag_pool_max);
1005 // Increment cheat prevention timers
1006 m_dig_pool.add(dtime);
1007 m_move_pool.add(dtime);
1008 m_time_from_last_punch += dtime;
1009 m_nocheat_dig_time += dtime;
1011 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1012 // If the object gets detached this comes into effect automatically from the last known origin
1014 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1015 m_last_good_position = pos;
1016 setBasePosition(pos);
1019 if (!send_recommended)
1022 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1023 if(m_position_not_sent && !isAttached())
1025 m_position_not_sent = false;
1026 float update_interval = m_env->getSendRecommendedInterval();
1028 if(isAttached()) // Just in case we ever do send attachment position too
1029 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1031 pos = m_base_position + v3f(0,BS*1,0);
1032 std::string str = gob_cmd_update_position(
1041 // create message and add to list
1042 ActiveObjectMessage aom(getId(), false, str);
1043 m_messages_out.push(aom);
1046 if (!m_armor_groups_sent) {
1047 m_armor_groups_sent = true;
1048 std::string str = gob_cmd_update_armor_groups(
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), true, str);
1052 m_messages_out.push(aom);
1055 if (!m_physics_override_sent) {
1056 m_physics_override_sent = true;
1057 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1058 m_physics_override_jump, m_physics_override_gravity,
1059 m_physics_override_sneak, m_physics_override_sneak_glitch);
1060 // create message and add to list
1061 ActiveObjectMessage aom(getId(), true, str);
1062 m_messages_out.push(aom);
1065 if (!m_animation_sent) {
1066 m_animation_sent = true;
1067 std::string str = gob_cmd_update_animation(
1068 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1069 // create message and add to list
1070 ActiveObjectMessage aom(getId(), true, str);
1071 m_messages_out.push(aom);
1074 if (!m_bone_position_sent) {
1075 m_bone_position_sent = true;
1076 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1077 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1078 std::string str = gob_cmd_update_bone_position((*ii).first,
1079 (*ii).second.X, (*ii).second.Y);
1080 // create message and add to list
1081 ActiveObjectMessage aom(getId(), true, str);
1082 m_messages_out.push(aom);
1086 if (!m_attachment_sent){
1087 m_attachment_sent = true;
1088 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1089 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1090 // create message and add to list
1091 ActiveObjectMessage aom(getId(), true, str);
1092 m_messages_out.push(aom);
1096 void PlayerSAO::setBasePosition(const v3f &position)
1098 if (m_player && position != m_base_position)
1099 m_player->setDirty(true);
1101 // This needs to be ran for attachments too
1102 ServerActiveObject::setBasePosition(position);
1103 m_position_not_sent = true;
1106 void PlayerSAO::setPos(const v3f &pos)
1111 setBasePosition(pos);
1112 // Movement caused by this command is always valid
1113 m_last_good_position = pos;
1114 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1117 void PlayerSAO::moveTo(v3f pos, bool continuous)
1122 setBasePosition(pos);
1123 // Movement caused by this command is always valid
1124 m_last_good_position = pos;
1125 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1128 void PlayerSAO::setYaw(const float yaw)
1130 if (m_player && yaw != m_yaw)
1131 m_player->setDirty(true);
1133 UnitSAO::setYaw(yaw);
1136 void PlayerSAO::setFov(const float fov)
1138 if (m_player && fov != m_fov)
1139 m_player->setDirty(true);
1144 void PlayerSAO::setWantedRange(const s16 range)
1146 if (m_player && range != m_wanted_range)
1147 m_player->setDirty(true);
1149 m_wanted_range = range;
1152 void PlayerSAO::setYawAndSend(const float yaw)
1155 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1158 void PlayerSAO::setPitch(const float pitch)
1160 if (m_player && pitch != m_pitch)
1161 m_player->setDirty(true);
1166 void PlayerSAO::setPitchAndSend(const float pitch)
1169 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1172 int PlayerSAO::punch(v3f dir,
1173 const ToolCapabilities *toolcap,
1174 ServerActiveObject *puncher,
1175 float time_from_last_punch)
1177 // It's best that attachments cannot be punched
1184 // No effect if PvP disabled
1185 if (g_settings->getBool("enable_pvp") == false) {
1186 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1187 std::string str = gob_cmd_punched(0, getHP());
1188 // create message and add to list
1189 ActiveObjectMessage aom(getId(), true, str);
1190 m_messages_out.push(aom);
1195 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1196 time_from_last_punch);
1198 std::string punchername = "nil";
1201 punchername = puncher->getDescription();
1203 PlayerSAO *playersao = m_player->getPlayerSAO();
1205 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1206 puncher, time_from_last_punch, toolcap, dir,
1209 if (!damage_handled) {
1210 setHP(getHP() - hitparams.hp);
1211 } else { // override client prediction
1212 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1213 std::string str = gob_cmd_punched(0, getHP());
1214 // create message and add to list
1215 ActiveObjectMessage aom(getId(), true, str);
1216 m_messages_out.push(aom);
1221 actionstream << "Player " << m_player->getName() << " punched by "
1223 if (!damage_handled) {
1224 actionstream << ", damage " << hitparams.hp << " HP";
1226 actionstream << ", damage handled by lua";
1228 actionstream << std::endl;
1230 return hitparams.wear;
1233 s16 PlayerSAO::readDamage()
1235 s16 damage = m_damage;
1240 void PlayerSAO::setHP(s16 hp)
1244 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1247 hp = oldhp + hp_change;
1251 else if (hp > PLAYER_MAX_HP)
1254 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1261 m_damage += (oldhp - hp);
1263 // Update properties on death
1264 if ((hp == 0) != (oldhp == 0))
1265 m_properties_sent = false;
1268 void PlayerSAO::setBreath(const u16 breath, bool send)
1270 if (m_player && breath != m_breath)
1271 m_player->setDirty(true);
1273 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1276 m_env->getGameDef()->SendPlayerBreath(this);
1279 Inventory* PlayerSAO::getInventory()
1283 const Inventory* PlayerSAO::getInventory() const
1288 InventoryLocation PlayerSAO::getInventoryLocation() const
1290 InventoryLocation loc;
1291 loc.setPlayer(m_player->getName());
1295 std::string PlayerSAO::getWieldList() const
1300 ItemStack PlayerSAO::getWieldedItem() const
1302 const Inventory *inv = getInventory();
1304 const InventoryList *mlist = inv->getList(getWieldList());
1305 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1306 ret = mlist->getItem(getWieldIndex());
1307 if (ret.name.empty()) {
1308 const InventoryList *hlist = inv->getList("hand");
1310 ret = hlist->getItem(0);
1315 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1317 Inventory *inv = getInventory();
1319 InventoryList *mlist = inv->getList(getWieldList());
1321 ItemStack olditem = mlist->getItem(getWieldIndex());
1322 if (olditem.name.empty()) {
1323 InventoryList *hlist = inv->getList("hand");
1325 hlist->changeItem(0, item);
1329 mlist->changeItem(getWieldIndex(), item);
1336 int PlayerSAO::getWieldIndex() const
1338 return m_wield_index;
1341 void PlayerSAO::setWieldIndex(int i)
1343 if(i != m_wield_index) {
1348 // Erase the peer id and make the object for removal
1349 void PlayerSAO::disconnected()
1355 void PlayerSAO::unlinkPlayerSessionAndSave()
1357 assert(m_player->getPlayerSAO() == this);
1358 m_player->peer_id = 0;
1359 m_env->savePlayer(m_player);
1360 m_player->setPlayerSAO(NULL);
1361 m_env->removePlayer(m_player);
1364 std::string PlayerSAO::getPropertyPacket()
1366 m_prop.is_visible = (true);
1367 return gob_cmd_set_properties(m_prop);
1370 bool PlayerSAO::checkMovementCheat()
1372 if (isAttached() || m_is_singleplayer ||
1373 g_settings->getBool("disable_anticheat")) {
1374 m_last_good_position = m_base_position;
1378 bool cheated = false;
1380 Check player movements
1382 NOTE: Actually the server should handle player physics like the
1383 client does and compare player's position to what is calculated
1384 on our side. This is required when eg. players fly due to an
1385 explosion. Altough a node-based alternative might be possible
1386 too, and much more lightweight.
1389 float player_max_speed = 0;
1391 if (m_privs.count("fast") != 0) {
1393 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1396 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1398 // Tolerance. The lag pool does this a bit.
1399 //player_max_speed *= 2.5;
1401 v3f diff = (m_base_position - m_last_good_position);
1402 float d_vert = diff.Y;
1404 float d_horiz = diff.getLength();
1405 float required_time = d_horiz / player_max_speed;
1407 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1408 required_time = d_vert / player_max_speed; // Moving upwards
1410 if (m_move_pool.grab(required_time)) {
1411 m_last_good_position = m_base_position;
1413 actionstream << "Player " << m_player->getName()
1414 << " moved too fast; resetting position"
1416 setBasePosition(m_last_good_position);
1422 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1424 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1425 toset->MinEdge += m_base_position;
1426 toset->MaxEdge += m_base_position;