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::addAttachmentChild(int child_id)
208 m_attachment_child_ids.insert(child_id);
211 void UnitSAO::removeAttachmentChild(int child_id)
213 m_attachment_child_ids.erase(child_id);
216 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 return m_attachment_child_ids;
221 ObjectProperties* UnitSAO::accessObjectProperties()
226 void UnitSAO::notifyObjectPropertiesModified()
228 m_properties_sent = false;
235 // Prototype (registers item for deserialization)
236 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
239 const std::string &name, const std::string &state):
245 m_acceleration(0,0,0),
247 m_last_sent_position(0,0,0),
248 m_last_sent_velocity(0,0,0),
249 m_last_sent_position_timer(0),
250 m_last_sent_move_precision(0),
251 m_current_texture_modifier("")
253 // Only register type if no environment supplied
255 ServerActiveObject::registerType(getType(), create);
260 LuaEntitySAO::~LuaEntitySAO()
263 m_env->getScriptIface()->luaentity_Remove(m_id);
266 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267 it != m_attached_particle_spawners.end(); ++it) {
268 m_env->deleteParticleSpawner(*it, false);
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
274 ServerActiveObject::addedToEnvironment(dtime_s);
276 // Create entity from name
277 m_registered = m_env->getScriptIface()->
278 luaentity_Add(m_id, m_init_name.c_str());
282 m_env->getScriptIface()->
283 luaentity_GetProperties(m_id, &m_prop);
284 // Initialize HP from properties
285 m_hp = m_prop.hp_max;
286 // Activate entity, supplying serialized state
287 m_env->getScriptIface()->
288 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
290 m_prop.infotext = m_init_name;
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295 const std::string &data)
303 std::istringstream is(data, std::ios::binary);
305 u8 version = readU8(is);
306 // check if version is supported
308 name = deSerializeString(is);
309 state = deSerializeLongString(is);
311 else if(version == 1){
312 name = deSerializeString(is);
313 state = deSerializeLongString(is);
315 velocity = readV3F1000(is);
320 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321 <<state<<"\")"<<std::endl;
322 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
324 sao->m_velocity = velocity;
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
331 if(!m_properties_sent)
333 m_properties_sent = true;
334 std::string str = getPropertyPacket();
335 // create message and add to list
336 ActiveObjectMessage aom(getId(), true, str);
337 m_messages_out.push(aom);
340 // If attached, check that our parent is still there. If it isn't, detach.
341 if(m_attachment_parent_id && !isAttached())
343 m_attachment_parent_id = 0;
344 m_attachment_bone = "";
345 m_attachment_position = v3f(0,0,0);
346 m_attachment_rotation = v3f(0,0,0);
347 sendPosition(false, true);
350 m_last_sent_position_timer += dtime;
352 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353 // If the object gets detached this comes into effect automatically from the last known origin
356 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357 m_base_position = pos;
358 m_velocity = v3f(0,0,0);
359 m_acceleration = v3f(0,0,0);
364 aabb3f box = m_prop.collisionbox;
367 collisionMoveResult moveresult;
368 f32 pos_max_d = BS*0.25; // Distance per iteration
369 v3f p_pos = m_base_position;
370 v3f p_velocity = m_velocity;
371 v3f p_acceleration = m_acceleration;
372 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373 pos_max_d, box, m_prop.stepheight, dtime,
374 &p_pos, &p_velocity, p_acceleration,
375 this, m_prop.collideWithObjects);
378 m_base_position = p_pos;
379 m_velocity = p_velocity;
380 m_acceleration = p_acceleration;
382 m_base_position += dtime * m_velocity + 0.5 * dtime
383 * dtime * m_acceleration;
384 m_velocity += dtime * m_acceleration;
387 if((m_prop.automatic_face_movement_dir) &&
388 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
390 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391 + m_prop.automatic_face_movement_dir_offset;
392 float max_rotation_delta =
393 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
395 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
398 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
406 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
409 if(send_recommended == false)
414 // TODO: force send when acceleration changes enough?
415 float minchange = 0.2*BS;
416 if(m_last_sent_position_timer > 1.0){
418 } else if(m_last_sent_position_timer > 0.2){
421 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422 move_d += m_last_sent_move_precision;
423 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424 if(move_d > minchange || vel_d > minchange ||
425 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426 sendPosition(true, false);
430 if(m_armor_groups_sent == false){
431 m_armor_groups_sent = true;
432 std::string str = gob_cmd_update_armor_groups(
434 // create message and add to list
435 ActiveObjectMessage aom(getId(), true, str);
436 m_messages_out.push(aom);
439 if(m_animation_sent == false){
440 m_animation_sent = true;
441 std::string str = gob_cmd_update_animation(
442 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push(aom);
448 if(m_bone_position_sent == false){
449 m_bone_position_sent = true;
450 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
451 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
452 std::string str = gob_cmd_update_bone_position((*ii).first,
453 (*ii).second.X, (*ii).second.Y);
454 // create message and add to list
455 ActiveObjectMessage aom(getId(), true, str);
456 m_messages_out.push(aom);
460 if(m_attachment_sent == false){
461 m_attachment_sent = true;
462 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push(aom);
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
471 std::ostringstream os(std::ios::binary);
473 if(protocol_version >= 14)
475 writeU8(os, 1); // version
476 os<<serializeString(""); // name
477 writeU8(os, 0); // is_player
478 writeS16(os, getId()); //id
479 writeV3F1000(os, m_base_position);
480 writeF1000(os, m_yaw);
483 std::ostringstream msg_os(std::ios::binary);
484 msg_os << serializeLongString(getPropertyPacket()); // message 1
485 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
486 msg_os << serializeLongString(gob_cmd_update_animation(
487 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
488 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
489 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
490 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
491 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
493 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
494 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
495 int message_count = 4 + m_bone_position.size();
496 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
497 (ii != m_attachment_child_ids.end()); ++ii) {
498 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
500 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
501 obj->getClientInitializationData(protocol_version)));
505 msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier));
508 writeU8(os, message_count);
509 os.write(msg_os.str().c_str(), msg_os.str().size());
513 writeU8(os, 0); // version
514 os<<serializeString(""); // name
515 writeU8(os, 0); // is_player
516 writeV3F1000(os, m_base_position);
517 writeF1000(os, m_yaw);
519 writeU8(os, 2); // number of messages stuffed in here
520 os<<serializeLongString(getPropertyPacket()); // message 1
521 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
528 void LuaEntitySAO::getStaticData(std::string *result) const
530 verbosestream<<FUNCTION_NAME<<std::endl;
531 std::ostringstream os(std::ios::binary);
535 os<<serializeString(m_init_name);
538 std::string state = m_env->getScriptIface()->
539 luaentity_GetStaticdata(m_id);
540 os<<serializeLongString(state);
542 os<<serializeLongString(m_init_state);
547 writeV3F1000(os, m_velocity);
549 writeF1000(os, m_yaw);
553 int LuaEntitySAO::punch(v3f dir,
554 const ToolCapabilities *toolcap,
555 ServerActiveObject *puncher,
556 float time_from_last_punch)
559 // Delete unknown LuaEntities when punched
564 // It's best that attachments cannot be punched
568 ItemStack *punchitem = NULL;
569 ItemStack punchitem_static;
571 punchitem_static = puncher->getWieldedItem();
572 punchitem = &punchitem_static;
575 PunchDamageResult result = getPunchDamage(
579 time_from_last_punch);
581 bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
582 time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
584 if (!damage_handled) {
585 if (result.did_punch) {
586 setHP(getHP() - result.damage);
588 if (result.damage > 0) {
589 std::string punchername = puncher ? puncher->getDescription() : "nil";
591 actionstream << getDescription() << " punched by "
592 << punchername << ", damage " << result.damage
593 << " hp, health now " << getHP() << " hp" << std::endl;
596 std::string str = gob_cmd_punched(result.damage, getHP());
597 // create message and add to list
598 ActiveObjectMessage aom(getId(), true, str);
599 m_messages_out.push(aom);
611 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
615 // It's best that attachments cannot be clicked
618 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
621 void LuaEntitySAO::setPos(const v3f &pos)
625 m_base_position = pos;
626 sendPosition(false, true);
629 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
633 m_base_position = pos;
635 sendPosition(true, true);
638 float LuaEntitySAO::getMinimumSavedMovement()
643 std::string LuaEntitySAO::getDescription()
645 std::ostringstream os(std::ios::binary);
646 os<<"LuaEntitySAO at (";
647 os<<(m_base_position.X/BS)<<",";
648 os<<(m_base_position.Y/BS)<<",";
649 os<<(m_base_position.Z/BS);
654 void LuaEntitySAO::setHP(s16 hp)
660 s16 LuaEntitySAO::getHP() const
665 void LuaEntitySAO::setVelocity(v3f velocity)
667 m_velocity = velocity;
670 v3f LuaEntitySAO::getVelocity()
675 void LuaEntitySAO::setAcceleration(v3f acceleration)
677 m_acceleration = acceleration;
680 v3f LuaEntitySAO::getAcceleration()
682 return m_acceleration;
685 void LuaEntitySAO::setTextureMod(const std::string &mod)
687 std::string str = gob_cmd_set_texture_mod(mod);
688 m_current_texture_modifier = mod;
689 // create message and add to list
690 ActiveObjectMessage aom(getId(), true, str);
691 m_messages_out.push(aom);
694 std::string LuaEntitySAO::getTextureMod() const
696 return m_current_texture_modifier;
699 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
700 bool select_horiz_by_yawpitch)
702 std::string str = gob_cmd_set_sprite(
706 select_horiz_by_yawpitch
708 // create message and add to list
709 ActiveObjectMessage aom(getId(), true, str);
710 m_messages_out.push(aom);
713 std::string LuaEntitySAO::getName()
718 std::string LuaEntitySAO::getPropertyPacket()
720 return gob_cmd_set_properties(m_prop);
723 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
725 // If the object is attached client-side, don't waste bandwidth sending its position to clients
729 m_last_sent_move_precision = m_base_position.getDistanceFrom(
730 m_last_sent_position);
731 m_last_sent_position_timer = 0;
732 m_last_sent_yaw = m_yaw;
733 m_last_sent_position = m_base_position;
734 m_last_sent_velocity = m_velocity;
735 //m_last_sent_acceleration = m_acceleration;
737 float update_interval = m_env->getSendRecommendedInterval();
739 std::string str = gob_cmd_update_position(
748 // create message and add to list
749 ActiveObjectMessage aom(getId(), false, str);
750 m_messages_out.push(aom);
753 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
757 //update collision box
758 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
759 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
761 toset->MinEdge += m_base_position;
762 toset->MaxEdge += m_base_position;
770 bool LuaEntitySAO::collideWithObjects() const
772 return m_prop.collideWithObjects;
779 // No prototype, PlayerSAO does not need to be deserialized
781 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
782 UnitSAO(env_, v3f(0,0,0)),
787 m_last_good_position(0,0,0),
788 m_time_from_last_punch(0),
789 m_nocheat_dig_pos(32767, 32767, 32767),
790 m_nocheat_dig_time(0),
792 m_position_not_sent(false),
793 m_is_singleplayer(is_singleplayer),
794 m_breath(PLAYER_MAX_BREATH),
798 m_extended_attributes_modified(false),
800 m_physics_override_speed(1),
801 m_physics_override_jump(1),
802 m_physics_override_gravity(1),
803 m_physics_override_sneak(true),
804 m_physics_override_sneak_glitch(true),
805 m_physics_override_sent(false)
807 assert(m_peer_id != 0); // pre-condition
809 m_prop.hp_max = PLAYER_MAX_HP;
810 m_prop.physical = false;
812 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
813 // start of default appearance, this should be overwritten by LUA
814 m_prop.visual = "upright_sprite";
815 m_prop.visual_size = v2f(1, 2);
816 m_prop.textures.clear();
817 m_prop.textures.push_back("player.png");
818 m_prop.textures.push_back("player_back.png");
819 m_prop.colors.clear();
820 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
821 m_prop.spritediv = v2s16(1,1);
822 // end of default appearance
823 m_prop.is_visible = true;
824 m_prop.makes_footstep_sound = true;
825 m_hp = PLAYER_MAX_HP;
828 PlayerSAO::~PlayerSAO()
830 if(m_inventory != &m_player->inventory)
834 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
839 m_inventory = &m_player->inventory;
842 v3f PlayerSAO::getEyeOffset() const
844 return v3f(0, BS * 1.625f, 0);
847 std::string PlayerSAO::getDescription()
849 return std::string("player ") + m_player->getName();
852 // Called after id has been set and has been inserted in environment
853 void PlayerSAO::addedToEnvironment(u32 dtime_s)
855 ServerActiveObject::addedToEnvironment(dtime_s);
856 ServerActiveObject::setBasePosition(m_base_position);
857 m_player->setPlayerSAO(this);
858 m_player->peer_id = m_peer_id;
859 m_last_good_position = m_base_position;
862 // Called before removing from environment
863 void PlayerSAO::removingFromEnvironment()
865 ServerActiveObject::removingFromEnvironment();
866 if (m_player->getPlayerSAO() == this) {
867 unlinkPlayerSessionAndSave();
868 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
869 it != m_attached_particle_spawners.end(); ++it) {
870 m_env->deleteParticleSpawner(*it, false);
875 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
877 std::ostringstream os(std::ios::binary);
879 if(protocol_version >= 15)
881 writeU8(os, 1); // version
882 os<<serializeString(m_player->getName()); // name
883 writeU8(os, 1); // is_player
884 writeS16(os, getId()); //id
885 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
886 writeF1000(os, m_yaw);
887 writeS16(os, getHP());
889 std::ostringstream msg_os(std::ios::binary);
890 msg_os << serializeLongString(getPropertyPacket()); // message 1
891 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
892 msg_os << serializeLongString(gob_cmd_update_animation(
893 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
894 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
895 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
896 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
897 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
899 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
900 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
901 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
902 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
903 m_physics_override_sneak_glitch)); // 5
904 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
905 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
906 int message_count = 6 + m_bone_position.size();
907 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
908 ii != m_attachment_child_ids.end(); ++ii) {
909 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
911 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
912 obj->getClientInitializationData(protocol_version)));
916 writeU8(os, message_count);
917 os.write(msg_os.str().c_str(), msg_os.str().size());
921 writeU8(os, 0); // version
922 os<<serializeString(m_player->getName()); // name
923 writeU8(os, 1); // is_player
924 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
925 writeF1000(os, m_yaw);
926 writeS16(os, getHP());
927 writeU8(os, 2); // number of messages stuffed in here
928 os<<serializeLongString(getPropertyPacket()); // message 1
929 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
936 void PlayerSAO::getStaticData(std::string *result) const
938 FATAL_ERROR("Deprecated function");
941 void PlayerSAO::step(float dtime, bool send_recommended)
943 if (m_drowning_interval.step(dtime, 2.0)) {
945 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
946 MapNode n = m_env->getMap().getNodeNoEx(p);
947 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
948 // If node generates drown
949 if (c.drowning > 0) {
950 if (m_hp > 0 && m_breath > 0)
951 setBreath(m_breath - 1);
953 // No more breath, damage player
955 setHP(m_hp - c.drowning);
956 m_env->getGameDef()->SendPlayerHPOrDie(this);
961 if (m_breathing_interval.step(dtime, 0.5)) {
963 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
964 MapNode n = m_env->getMap().getNodeNoEx(p);
965 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
966 // If player is alive & no drowning, breath
967 if (m_hp > 0 && c.drowning == 0)
968 setBreath(m_breath + 1);
971 if (!m_properties_sent) {
972 m_properties_sent = true;
973 std::string str = getPropertyPacket();
974 // create message and add to list
975 ActiveObjectMessage aom(getId(), true, str);
976 m_messages_out.push(aom);
979 // If attached, check that our parent is still there. If it isn't, detach.
980 if(m_attachment_parent_id && !isAttached())
982 m_attachment_parent_id = 0;
983 m_attachment_bone = "";
984 m_attachment_position = v3f(0,0,0);
985 m_attachment_rotation = v3f(0,0,0);
986 setBasePosition(m_last_good_position);
987 m_env->getGameDef()->SendMovePlayer(m_peer_id);
990 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
992 // Set lag pool maximums based on estimated lag
993 const float LAG_POOL_MIN = 5.0;
994 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
995 if(lag_pool_max < LAG_POOL_MIN)
996 lag_pool_max = LAG_POOL_MIN;
997 m_dig_pool.setMax(lag_pool_max);
998 m_move_pool.setMax(lag_pool_max);
1000 // Increment cheat prevention timers
1001 m_dig_pool.add(dtime);
1002 m_move_pool.add(dtime);
1003 m_time_from_last_punch += dtime;
1004 m_nocheat_dig_time += dtime;
1006 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1007 // If the object gets detached this comes into effect automatically from the last known origin
1009 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1010 m_last_good_position = pos;
1011 setBasePosition(pos);
1014 if (!send_recommended)
1017 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1018 if(m_position_not_sent && !isAttached())
1020 m_position_not_sent = false;
1021 float update_interval = m_env->getSendRecommendedInterval();
1023 if(isAttached()) // Just in case we ever do send attachment position too
1024 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1026 pos = m_base_position + v3f(0,BS*1,0);
1027 std::string str = gob_cmd_update_position(
1036 // create message and add to list
1037 ActiveObjectMessage aom(getId(), false, str);
1038 m_messages_out.push(aom);
1041 if (!m_armor_groups_sent) {
1042 m_armor_groups_sent = true;
1043 std::string str = gob_cmd_update_armor_groups(
1045 // create message and add to list
1046 ActiveObjectMessage aom(getId(), true, str);
1047 m_messages_out.push(aom);
1050 if (!m_physics_override_sent) {
1051 m_physics_override_sent = true;
1052 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1053 m_physics_override_jump, m_physics_override_gravity,
1054 m_physics_override_sneak, m_physics_override_sneak_glitch);
1055 // create message and add to list
1056 ActiveObjectMessage aom(getId(), true, str);
1057 m_messages_out.push(aom);
1060 if (!m_animation_sent) {
1061 m_animation_sent = true;
1062 std::string str = gob_cmd_update_animation(
1063 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1064 // create message and add to list
1065 ActiveObjectMessage aom(getId(), true, str);
1066 m_messages_out.push(aom);
1069 if (!m_bone_position_sent) {
1070 m_bone_position_sent = true;
1071 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1072 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1073 std::string str = gob_cmd_update_bone_position((*ii).first,
1074 (*ii).second.X, (*ii).second.Y);
1075 // create message and add to list
1076 ActiveObjectMessage aom(getId(), true, str);
1077 m_messages_out.push(aom);
1081 if (!m_attachment_sent){
1082 m_attachment_sent = true;
1083 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1084 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1085 // create message and add to list
1086 ActiveObjectMessage aom(getId(), true, str);
1087 m_messages_out.push(aom);
1091 void PlayerSAO::setBasePosition(const v3f &position)
1093 if (m_player && position != m_base_position)
1094 m_player->setDirty(true);
1096 // This needs to be ran for attachments too
1097 ServerActiveObject::setBasePosition(position);
1098 m_position_not_sent = true;
1101 void PlayerSAO::setPos(const v3f &pos)
1106 setBasePosition(pos);
1107 // Movement caused by this command is always valid
1108 m_last_good_position = pos;
1109 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1112 void PlayerSAO::moveTo(v3f pos, bool continuous)
1117 setBasePosition(pos);
1118 // Movement caused by this command is always valid
1119 m_last_good_position = pos;
1120 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1123 void PlayerSAO::setYaw(const float yaw)
1125 if (m_player && yaw != m_yaw)
1126 m_player->setDirty(true);
1128 UnitSAO::setYaw(yaw);
1131 void PlayerSAO::setFov(const float fov)
1133 if (m_player && fov != m_fov)
1134 m_player->setDirty(true);
1139 void PlayerSAO::setWantedRange(const s16 range)
1141 if (m_player && range != m_wanted_range)
1142 m_player->setDirty(true);
1144 m_wanted_range = range;
1147 void PlayerSAO::setYawAndSend(const float yaw)
1150 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1153 void PlayerSAO::setPitch(const float pitch)
1155 if (m_player && pitch != m_pitch)
1156 m_player->setDirty(true);
1161 void PlayerSAO::setPitchAndSend(const float pitch)
1164 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1167 int PlayerSAO::punch(v3f dir,
1168 const ToolCapabilities *toolcap,
1169 ServerActiveObject *puncher,
1170 float time_from_last_punch)
1172 // It's best that attachments cannot be punched
1179 // No effect if PvP disabled
1180 if (g_settings->getBool("enable_pvp") == false) {
1181 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1182 std::string str = gob_cmd_punched(0, getHP());
1183 // create message and add to list
1184 ActiveObjectMessage aom(getId(), true, str);
1185 m_messages_out.push(aom);
1190 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1191 time_from_last_punch);
1193 std::string punchername = "nil";
1196 punchername = puncher->getDescription();
1198 PlayerSAO *playersao = m_player->getPlayerSAO();
1200 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1201 puncher, time_from_last_punch, toolcap, dir,
1204 if (!damage_handled) {
1205 setHP(getHP() - hitparams.hp);
1206 } else { // override client prediction
1207 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1208 std::string str = gob_cmd_punched(0, getHP());
1209 // create message and add to list
1210 ActiveObjectMessage aom(getId(), true, str);
1211 m_messages_out.push(aom);
1216 actionstream << "Player " << m_player->getName() << " punched by "
1218 if (!damage_handled) {
1219 actionstream << ", damage " << hitparams.hp << " HP";
1221 actionstream << ", damage handled by lua";
1223 actionstream << std::endl;
1225 return hitparams.wear;
1228 s16 PlayerSAO::readDamage()
1230 s16 damage = m_damage;
1235 void PlayerSAO::setHP(s16 hp)
1239 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1242 hp = oldhp + hp_change;
1246 else if (hp > PLAYER_MAX_HP)
1249 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1256 m_damage += (oldhp - hp);
1258 // Update properties on death
1259 if ((hp == 0) != (oldhp == 0))
1260 m_properties_sent = false;
1263 void PlayerSAO::setBreath(const u16 breath, bool send)
1265 if (m_player && breath != m_breath)
1266 m_player->setDirty(true);
1268 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1271 m_env->getGameDef()->SendPlayerBreath(this);
1274 Inventory* PlayerSAO::getInventory()
1278 const Inventory* PlayerSAO::getInventory() const
1283 InventoryLocation PlayerSAO::getInventoryLocation() const
1285 InventoryLocation loc;
1286 loc.setPlayer(m_player->getName());
1290 std::string PlayerSAO::getWieldList() const
1295 ItemStack PlayerSAO::getWieldedItem() const
1297 const Inventory *inv = getInventory();
1299 const InventoryList *mlist = inv->getList(getWieldList());
1300 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1301 ret = mlist->getItem(getWieldIndex());
1302 if (ret.name.empty()) {
1303 const InventoryList *hlist = inv->getList("hand");
1305 ret = hlist->getItem(0);
1310 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1312 Inventory *inv = getInventory();
1314 InventoryList *mlist = inv->getList(getWieldList());
1316 ItemStack olditem = mlist->getItem(getWieldIndex());
1317 if (olditem.name.empty()) {
1318 InventoryList *hlist = inv->getList("hand");
1320 hlist->changeItem(0, item);
1324 mlist->changeItem(getWieldIndex(), item);
1331 int PlayerSAO::getWieldIndex() const
1333 return m_wield_index;
1336 void PlayerSAO::setWieldIndex(int i)
1338 if(i != m_wield_index) {
1343 // Erase the peer id and make the object for removal
1344 void PlayerSAO::disconnected()
1350 void PlayerSAO::unlinkPlayerSessionAndSave()
1352 assert(m_player->getPlayerSAO() == this);
1353 m_player->peer_id = 0;
1354 m_env->savePlayer(m_player);
1355 m_player->setPlayerSAO(NULL);
1356 m_env->removePlayer(m_player);
1359 std::string PlayerSAO::getPropertyPacket()
1361 m_prop.is_visible = (true);
1362 return gob_cmd_set_properties(m_prop);
1365 bool PlayerSAO::checkMovementCheat()
1367 if (isAttached() || m_is_singleplayer ||
1368 g_settings->getBool("disable_anticheat")) {
1369 m_last_good_position = m_base_position;
1373 bool cheated = false;
1375 Check player movements
1377 NOTE: Actually the server should handle player physics like the
1378 client does and compare player's position to what is calculated
1379 on our side. This is required when eg. players fly due to an
1380 explosion. Altough a node-based alternative might be possible
1381 too, and much more lightweight.
1384 float player_max_speed = 0;
1386 if (m_privs.count("fast") != 0) {
1388 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1391 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1393 // Tolerance. The lag pool does this a bit.
1394 //player_max_speed *= 2.5;
1396 v3f diff = (m_base_position - m_last_good_position);
1397 float d_vert = diff.Y;
1399 float d_horiz = diff.getLength();
1400 float required_time = d_horiz / player_max_speed;
1402 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1403 required_time = d_vert / player_max_speed; // Moving upwards
1405 if (m_move_pool.grab(required_time)) {
1406 m_last_good_position = m_base_position;
1408 actionstream << "Player " << m_player->getName()
1409 << " moved too fast; resetting position"
1411 setBasePosition(m_last_good_position);
1417 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1419 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1420 toset->MinEdge += m_base_position;
1421 toset->MaxEdge += m_base_position;