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)
252 // Only register type if no environment supplied
254 ServerActiveObject::registerType(getType(), create);
259 LuaEntitySAO::~LuaEntitySAO()
262 m_env->getScriptIface()->luaentity_Remove(m_id);
265 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
266 it != m_attached_particle_spawners.end(); ++it) {
267 m_env->deleteParticleSpawner(*it, false);
271 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
273 ServerActiveObject::addedToEnvironment(dtime_s);
275 // Create entity from name
276 m_registered = m_env->getScriptIface()->
277 luaentity_Add(m_id, m_init_name.c_str());
281 m_env->getScriptIface()->
282 luaentity_GetProperties(m_id, &m_prop);
283 // Initialize HP from properties
284 m_hp = m_prop.hp_max;
285 // Activate entity, supplying serialized state
286 m_env->getScriptIface()->
287 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
289 m_prop.infotext = m_init_name;
293 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
294 const std::string &data)
302 std::istringstream is(data, std::ios::binary);
304 u8 version = readU8(is);
305 // check if version is supported
307 name = deSerializeString(is);
308 state = deSerializeLongString(is);
310 else if(version == 1){
311 name = deSerializeString(is);
312 state = deSerializeLongString(is);
314 velocity = readV3F1000(is);
319 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
320 <<state<<"\")"<<std::endl;
321 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
323 sao->m_velocity = velocity;
328 void LuaEntitySAO::step(float dtime, bool send_recommended)
330 if(!m_properties_sent)
332 m_properties_sent = true;
333 std::string str = getPropertyPacket();
334 // create message and add to list
335 ActiveObjectMessage aom(getId(), true, str);
336 m_messages_out.push(aom);
339 // If attached, check that our parent is still there. If it isn't, detach.
340 if(m_attachment_parent_id && !isAttached())
342 m_attachment_parent_id = 0;
343 m_attachment_bone = "";
344 m_attachment_position = v3f(0,0,0);
345 m_attachment_rotation = v3f(0,0,0);
346 sendPosition(false, true);
349 m_last_sent_position_timer += dtime;
351 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
352 // If the object gets detached this comes into effect automatically from the last known origin
355 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
356 m_base_position = pos;
357 m_velocity = v3f(0,0,0);
358 m_acceleration = v3f(0,0,0);
363 aabb3f box = m_prop.collisionbox;
366 collisionMoveResult moveresult;
367 f32 pos_max_d = BS*0.25; // Distance per iteration
368 v3f p_pos = m_base_position;
369 v3f p_velocity = m_velocity;
370 v3f p_acceleration = m_acceleration;
371 moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
372 pos_max_d, box, m_prop.stepheight, dtime,
373 &p_pos, &p_velocity, p_acceleration,
374 this, m_prop.collideWithObjects);
377 m_base_position = p_pos;
378 m_velocity = p_velocity;
379 m_acceleration = p_acceleration;
381 m_base_position += dtime * m_velocity + 0.5 * dtime
382 * dtime * m_acceleration;
383 m_velocity += dtime * m_acceleration;
386 if((m_prop.automatic_face_movement_dir) &&
387 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
389 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
390 + m_prop.automatic_face_movement_dir_offset;
391 float max_rotation_delta =
392 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
394 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
395 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
397 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
405 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
408 if(send_recommended == false)
413 // TODO: force send when acceleration changes enough?
414 float minchange = 0.2*BS;
415 if(m_last_sent_position_timer > 1.0){
417 } else if(m_last_sent_position_timer > 0.2){
420 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
421 move_d += m_last_sent_move_precision;
422 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
423 if(move_d > minchange || vel_d > minchange ||
424 fabs(m_yaw - m_last_sent_yaw) > 1.0){
425 sendPosition(true, false);
429 if(m_armor_groups_sent == false){
430 m_armor_groups_sent = true;
431 std::string str = gob_cmd_update_armor_groups(
433 // create message and add to list
434 ActiveObjectMessage aom(getId(), true, str);
435 m_messages_out.push(aom);
438 if(m_animation_sent == false){
439 m_animation_sent = true;
440 std::string str = gob_cmd_update_animation(
441 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
442 // create message and add to list
443 ActiveObjectMessage aom(getId(), true, str);
444 m_messages_out.push(aom);
447 if(m_bone_position_sent == false){
448 m_bone_position_sent = true;
449 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
450 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
451 std::string str = gob_cmd_update_bone_position((*ii).first,
452 (*ii).second.X, (*ii).second.Y);
453 // create message and add to list
454 ActiveObjectMessage aom(getId(), true, str);
455 m_messages_out.push(aom);
459 if(m_attachment_sent == false){
460 m_attachment_sent = true;
461 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
462 // create message and add to list
463 ActiveObjectMessage aom(getId(), true, str);
464 m_messages_out.push(aom);
468 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
470 std::ostringstream os(std::ios::binary);
472 if(protocol_version >= 14)
474 writeU8(os, 1); // version
475 os<<serializeString(""); // name
476 writeU8(os, 0); // is_player
477 writeS16(os, getId()); //id
478 writeV3F1000(os, m_base_position);
479 writeF1000(os, m_yaw);
482 std::ostringstream msg_os(std::ios::binary);
483 msg_os << serializeLongString(getPropertyPacket()); // message 1
484 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
485 msg_os << serializeLongString(gob_cmd_update_animation(
486 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
487 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
488 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
489 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
490 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
492 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
493 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
494 int message_count = 4 + m_bone_position.size();
495 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
496 (ii != m_attachment_child_ids.end()); ++ii) {
497 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
499 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
500 obj->getClientInitializationData(protocol_version)));
504 writeU8(os, message_count);
505 os.write(msg_os.str().c_str(), msg_os.str().size());
509 writeU8(os, 0); // version
510 os<<serializeString(""); // name
511 writeU8(os, 0); // is_player
512 writeV3F1000(os, m_base_position);
513 writeF1000(os, m_yaw);
515 writeU8(os, 2); // number of messages stuffed in here
516 os<<serializeLongString(getPropertyPacket()); // message 1
517 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
524 void LuaEntitySAO::getStaticData(std::string *result) const
526 verbosestream<<FUNCTION_NAME<<std::endl;
527 std::ostringstream os(std::ios::binary);
531 os<<serializeString(m_init_name);
534 std::string state = m_env->getScriptIface()->
535 luaentity_GetStaticdata(m_id);
536 os<<serializeLongString(state);
538 os<<serializeLongString(m_init_state);
543 writeV3F1000(os, m_velocity);
545 writeF1000(os, m_yaw);
549 int LuaEntitySAO::punch(v3f dir,
550 const ToolCapabilities *toolcap,
551 ServerActiveObject *puncher,
552 float time_from_last_punch)
555 // Delete unknown LuaEntities when punched
560 // It's best that attachments cannot be punched
564 ItemStack *punchitem = NULL;
565 ItemStack punchitem_static;
567 punchitem_static = puncher->getWieldedItem();
568 punchitem = &punchitem_static;
571 PunchDamageResult result = getPunchDamage(
575 time_from_last_punch);
577 if (result.did_punch) {
578 setHP(getHP() - result.damage);
580 if (result.damage > 0) {
581 std::string punchername = puncher ? puncher->getDescription() : "nil";
583 actionstream << getDescription() << " punched by "
584 << punchername << ", damage " << result.damage
585 << " hp, health now " << getHP() << " hp" << std::endl;
588 std::string str = gob_cmd_punched(result.damage, getHP());
589 // create message and add to list
590 ActiveObjectMessage aom(getId(), true, str);
591 m_messages_out.push(aom);
597 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
598 time_from_last_punch, toolcap, dir);
603 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
607 // It's best that attachments cannot be clicked
610 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
613 void LuaEntitySAO::setPos(const v3f &pos)
617 m_base_position = pos;
618 sendPosition(false, true);
621 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
625 m_base_position = pos;
627 sendPosition(true, true);
630 float LuaEntitySAO::getMinimumSavedMovement()
635 std::string LuaEntitySAO::getDescription()
637 std::ostringstream os(std::ios::binary);
638 os<<"LuaEntitySAO at (";
639 os<<(m_base_position.X/BS)<<",";
640 os<<(m_base_position.Y/BS)<<",";
641 os<<(m_base_position.Z/BS);
646 void LuaEntitySAO::setHP(s16 hp)
652 s16 LuaEntitySAO::getHP() const
657 void LuaEntitySAO::setVelocity(v3f velocity)
659 m_velocity = velocity;
662 v3f LuaEntitySAO::getVelocity()
667 void LuaEntitySAO::setAcceleration(v3f acceleration)
669 m_acceleration = acceleration;
672 v3f LuaEntitySAO::getAcceleration()
674 return m_acceleration;
677 void LuaEntitySAO::setTextureMod(const std::string &mod)
679 std::string str = gob_cmd_set_texture_mod(mod);
680 // create message and add to list
681 ActiveObjectMessage aom(getId(), true, str);
682 m_messages_out.push(aom);
685 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
686 bool select_horiz_by_yawpitch)
688 std::string str = gob_cmd_set_sprite(
692 select_horiz_by_yawpitch
694 // create message and add to list
695 ActiveObjectMessage aom(getId(), true, str);
696 m_messages_out.push(aom);
699 std::string LuaEntitySAO::getName()
704 std::string LuaEntitySAO::getPropertyPacket()
706 return gob_cmd_set_properties(m_prop);
709 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
711 // If the object is attached client-side, don't waste bandwidth sending its position to clients
715 m_last_sent_move_precision = m_base_position.getDistanceFrom(
716 m_last_sent_position);
717 m_last_sent_position_timer = 0;
718 m_last_sent_yaw = m_yaw;
719 m_last_sent_position = m_base_position;
720 m_last_sent_velocity = m_velocity;
721 //m_last_sent_acceleration = m_acceleration;
723 float update_interval = m_env->getSendRecommendedInterval();
725 std::string str = gob_cmd_update_position(
734 // create message and add to list
735 ActiveObjectMessage aom(getId(), false, str);
736 m_messages_out.push(aom);
739 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
743 //update collision box
744 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
745 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
747 toset->MinEdge += m_base_position;
748 toset->MaxEdge += m_base_position;
756 bool LuaEntitySAO::collideWithObjects() const
758 return m_prop.collideWithObjects;
765 // No prototype, PlayerSAO does not need to be deserialized
767 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
768 UnitSAO(env_, v3f(0,0,0)),
773 m_last_good_position(0,0,0),
774 m_time_from_last_punch(0),
775 m_nocheat_dig_pos(32767, 32767, 32767),
776 m_nocheat_dig_time(0),
778 m_position_not_sent(false),
779 m_is_singleplayer(is_singleplayer),
780 m_breath(PLAYER_MAX_BREATH),
785 m_physics_override_speed(1),
786 m_physics_override_jump(1),
787 m_physics_override_gravity(1),
788 m_physics_override_sneak(true),
789 m_physics_override_sneak_glitch(true),
790 m_physics_override_sent(false)
792 assert(m_peer_id != 0); // pre-condition
794 m_prop.hp_max = PLAYER_MAX_HP;
795 m_prop.physical = false;
797 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
798 // start of default appearance, this should be overwritten by LUA
799 m_prop.visual = "upright_sprite";
800 m_prop.visual_size = v2f(1, 2);
801 m_prop.textures.clear();
802 m_prop.textures.push_back("player.png");
803 m_prop.textures.push_back("player_back.png");
804 m_prop.colors.clear();
805 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
806 m_prop.spritediv = v2s16(1,1);
807 // end of default appearance
808 m_prop.is_visible = true;
809 m_prop.makes_footstep_sound = true;
810 m_hp = PLAYER_MAX_HP;
813 PlayerSAO::~PlayerSAO()
815 if(m_inventory != &m_player->inventory)
819 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
824 m_inventory = &m_player->inventory;
827 v3f PlayerSAO::getEyeOffset() const
829 return v3f(0, BS * 1.625f, 0);
832 std::string PlayerSAO::getDescription()
834 return std::string("player ") + m_player->getName();
837 // Called after id has been set and has been inserted in environment
838 void PlayerSAO::addedToEnvironment(u32 dtime_s)
840 ServerActiveObject::addedToEnvironment(dtime_s);
841 ServerActiveObject::setBasePosition(m_base_position);
842 m_player->setPlayerSAO(this);
843 m_player->peer_id = m_peer_id;
844 m_last_good_position = m_base_position;
847 // Called before removing from environment
848 void PlayerSAO::removingFromEnvironment()
850 ServerActiveObject::removingFromEnvironment();
851 if (m_player->getPlayerSAO() == this) {
852 unlinkPlayerSessionAndSave();
853 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
854 it != m_attached_particle_spawners.end(); ++it) {
855 m_env->deleteParticleSpawner(*it, false);
860 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
862 std::ostringstream os(std::ios::binary);
864 if(protocol_version >= 15)
866 writeU8(os, 1); // version
867 os<<serializeString(m_player->getName()); // name
868 writeU8(os, 1); // is_player
869 writeS16(os, getId()); //id
870 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
871 writeF1000(os, m_yaw);
872 writeS16(os, getHP());
874 std::ostringstream msg_os(std::ios::binary);
875 msg_os << serializeLongString(getPropertyPacket()); // message 1
876 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
877 msg_os << serializeLongString(gob_cmd_update_animation(
878 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
879 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
880 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
881 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
882 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
884 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
885 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
886 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
887 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
888 m_physics_override_sneak_glitch)); // 5
889 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
890 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
891 int message_count = 6 + m_bone_position.size();
892 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
893 ii != m_attachment_child_ids.end(); ++ii) {
894 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
896 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
897 obj->getClientInitializationData(protocol_version)));
901 writeU8(os, message_count);
902 os.write(msg_os.str().c_str(), msg_os.str().size());
906 writeU8(os, 0); // version
907 os<<serializeString(m_player->getName()); // name
908 writeU8(os, 1); // is_player
909 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
910 writeF1000(os, m_yaw);
911 writeS16(os, getHP());
912 writeU8(os, 2); // number of messages stuffed in here
913 os<<serializeLongString(getPropertyPacket()); // message 1
914 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
921 void PlayerSAO::getStaticData(std::string *result) const
923 FATAL_ERROR("Deprecated function");
926 void PlayerSAO::step(float dtime, bool send_recommended)
928 if (m_drowning_interval.step(dtime, 2.0)) {
930 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
931 MapNode n = m_env->getMap().getNodeNoEx(p);
932 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
933 // If node generates drown
934 if (c.drowning > 0) {
935 if (m_hp > 0 && m_breath > 0)
936 setBreath(m_breath - 1);
938 // No more breath, damage player
940 setHP(m_hp - c.drowning);
941 m_env->getGameDef()->SendPlayerHPOrDie(this);
946 if (m_breathing_interval.step(dtime, 0.5)) {
948 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
949 MapNode n = m_env->getMap().getNodeNoEx(p);
950 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
951 // If player is alive & no drowning, breath
952 if (m_hp > 0 && c.drowning == 0)
953 setBreath(m_breath + 1);
956 if (!m_properties_sent) {
957 m_properties_sent = true;
958 std::string str = getPropertyPacket();
959 // create message and add to list
960 ActiveObjectMessage aom(getId(), true, str);
961 m_messages_out.push(aom);
964 // If attached, check that our parent is still there. If it isn't, detach.
965 if(m_attachment_parent_id && !isAttached())
967 m_attachment_parent_id = 0;
968 m_attachment_bone = "";
969 m_attachment_position = v3f(0,0,0);
970 m_attachment_rotation = v3f(0,0,0);
971 setBasePosition(m_last_good_position);
972 m_env->getGameDef()->SendMovePlayer(m_peer_id);
975 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
977 // Set lag pool maximums based on estimated lag
978 const float LAG_POOL_MIN = 5.0;
979 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
980 if(lag_pool_max < LAG_POOL_MIN)
981 lag_pool_max = LAG_POOL_MIN;
982 m_dig_pool.setMax(lag_pool_max);
983 m_move_pool.setMax(lag_pool_max);
985 // Increment cheat prevention timers
986 m_dig_pool.add(dtime);
987 m_move_pool.add(dtime);
988 m_time_from_last_punch += dtime;
989 m_nocheat_dig_time += dtime;
991 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
992 // If the object gets detached this comes into effect automatically from the last known origin
994 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
995 m_last_good_position = pos;
996 setBasePosition(pos);
999 if (!send_recommended)
1002 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1003 if(m_position_not_sent && !isAttached())
1005 m_position_not_sent = false;
1006 float update_interval = m_env->getSendRecommendedInterval();
1008 if(isAttached()) // Just in case we ever do send attachment position too
1009 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1011 pos = m_base_position + v3f(0,BS*1,0);
1012 std::string str = gob_cmd_update_position(
1021 // create message and add to list
1022 ActiveObjectMessage aom(getId(), false, str);
1023 m_messages_out.push(aom);
1026 if (!m_armor_groups_sent) {
1027 m_armor_groups_sent = true;
1028 std::string str = gob_cmd_update_armor_groups(
1030 // create message and add to list
1031 ActiveObjectMessage aom(getId(), true, str);
1032 m_messages_out.push(aom);
1035 if (!m_physics_override_sent) {
1036 m_physics_override_sent = true;
1037 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1038 m_physics_override_jump, m_physics_override_gravity,
1039 m_physics_override_sneak, m_physics_override_sneak_glitch);
1040 // create message and add to list
1041 ActiveObjectMessage aom(getId(), true, str);
1042 m_messages_out.push(aom);
1045 if (!m_animation_sent) {
1046 m_animation_sent = true;
1047 std::string str = gob_cmd_update_animation(
1048 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1049 // create message and add to list
1050 ActiveObjectMessage aom(getId(), true, str);
1051 m_messages_out.push(aom);
1054 if (!m_bone_position_sent) {
1055 m_bone_position_sent = true;
1056 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1057 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1058 std::string str = gob_cmd_update_bone_position((*ii).first,
1059 (*ii).second.X, (*ii).second.Y);
1060 // create message and add to list
1061 ActiveObjectMessage aom(getId(), true, str);
1062 m_messages_out.push(aom);
1066 if (!m_attachment_sent){
1067 m_attachment_sent = true;
1068 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1069 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1070 // create message and add to list
1071 ActiveObjectMessage aom(getId(), true, str);
1072 m_messages_out.push(aom);
1076 void PlayerSAO::setBasePosition(const v3f &position)
1078 if (m_player && position != m_base_position)
1079 m_player->setDirty(true);
1081 // This needs to be ran for attachments too
1082 ServerActiveObject::setBasePosition(position);
1083 m_position_not_sent = true;
1086 void PlayerSAO::setPos(const v3f &pos)
1091 setBasePosition(pos);
1092 // Movement caused by this command is always valid
1093 m_last_good_position = pos;
1094 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1097 void PlayerSAO::moveTo(v3f pos, bool continuous)
1102 setBasePosition(pos);
1103 // Movement caused by this command is always valid
1104 m_last_good_position = pos;
1105 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1108 void PlayerSAO::setYaw(const float yaw)
1110 if (m_player && yaw != m_yaw)
1111 m_player->setDirty(true);
1113 UnitSAO::setYaw(yaw);
1116 void PlayerSAO::setFov(const float fov)
1118 if (m_player && fov != m_fov)
1119 m_player->setDirty(true);
1124 void PlayerSAO::setWantedRange(const s16 range)
1126 if (m_player && range != m_wanted_range)
1127 m_player->setDirty(true);
1129 m_wanted_range = range;
1132 void PlayerSAO::setYawAndSend(const float yaw)
1135 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1138 void PlayerSAO::setPitch(const float pitch)
1140 if (m_player && pitch != m_pitch)
1141 m_player->setDirty(true);
1146 void PlayerSAO::setPitchAndSend(const float pitch)
1149 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1152 int PlayerSAO::punch(v3f dir,
1153 const ToolCapabilities *toolcap,
1154 ServerActiveObject *puncher,
1155 float time_from_last_punch)
1157 // It's best that attachments cannot be punched
1164 // No effect if PvP disabled
1165 if (g_settings->getBool("enable_pvp") == false) {
1166 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1167 std::string str = gob_cmd_punched(0, getHP());
1168 // create message and add to list
1169 ActiveObjectMessage aom(getId(), true, str);
1170 m_messages_out.push(aom);
1175 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1176 time_from_last_punch);
1178 std::string punchername = "nil";
1181 punchername = puncher->getDescription();
1183 PlayerSAO *playersao = m_player->getPlayerSAO();
1185 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1186 puncher, time_from_last_punch, toolcap, dir,
1189 if (!damage_handled) {
1190 setHP(getHP() - hitparams.hp);
1191 } else { // override client prediction
1192 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1193 std::string str = gob_cmd_punched(0, getHP());
1194 // create message and add to list
1195 ActiveObjectMessage aom(getId(), true, str);
1196 m_messages_out.push(aom);
1201 actionstream << "Player " << m_player->getName() << " punched by "
1203 if (!damage_handled) {
1204 actionstream << ", damage " << hitparams.hp << " HP";
1206 actionstream << ", damage handled by lua";
1208 actionstream << std::endl;
1210 return hitparams.wear;
1213 s16 PlayerSAO::readDamage()
1215 s16 damage = m_damage;
1220 void PlayerSAO::setHP(s16 hp)
1224 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1227 hp = oldhp + hp_change;
1231 else if (hp > PLAYER_MAX_HP)
1234 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1241 m_damage += (oldhp - hp);
1243 // Update properties on death
1244 if ((hp == 0) != (oldhp == 0))
1245 m_properties_sent = false;
1248 void PlayerSAO::setBreath(const u16 breath, bool send)
1250 if (m_player && breath != m_breath)
1251 m_player->setDirty(true);
1253 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1256 m_env->getGameDef()->SendPlayerBreath(this);
1259 Inventory* PlayerSAO::getInventory()
1263 const Inventory* PlayerSAO::getInventory() const
1268 InventoryLocation PlayerSAO::getInventoryLocation() const
1270 InventoryLocation loc;
1271 loc.setPlayer(m_player->getName());
1275 std::string PlayerSAO::getWieldList() const
1280 ItemStack PlayerSAO::getWieldedItem() const
1282 const Inventory *inv = getInventory();
1284 const InventoryList *mlist = inv->getList(getWieldList());
1285 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1286 ret = mlist->getItem(getWieldIndex());
1287 if (ret.name.empty()) {
1288 const InventoryList *hlist = inv->getList("hand");
1290 ret = hlist->getItem(0);
1295 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1297 Inventory *inv = getInventory();
1299 InventoryList *mlist = inv->getList(getWieldList());
1301 ItemStack olditem = mlist->getItem(getWieldIndex());
1302 if (olditem.name.empty()) {
1303 InventoryList *hlist = inv->getList("hand");
1305 hlist->changeItem(0, item);
1309 mlist->changeItem(getWieldIndex(), item);
1316 int PlayerSAO::getWieldIndex() const
1318 return m_wield_index;
1321 void PlayerSAO::setWieldIndex(int i)
1323 if(i != m_wield_index) {
1328 // Erase the peer id and make the object for removal
1329 void PlayerSAO::disconnected()
1335 void PlayerSAO::unlinkPlayerSessionAndSave()
1337 assert(m_player->getPlayerSAO() == this);
1338 m_player->peer_id = 0;
1339 m_env->savePlayer(m_player);
1340 m_player->setPlayerSAO(NULL);
1341 m_env->removePlayer(m_player);
1344 std::string PlayerSAO::getPropertyPacket()
1346 m_prop.is_visible = (true);
1347 return gob_cmd_set_properties(m_prop);
1350 bool PlayerSAO::checkMovementCheat()
1352 if (isAttached() || m_is_singleplayer ||
1353 g_settings->getBool("disable_anticheat")) {
1354 m_last_good_position = m_base_position;
1358 bool cheated = false;
1360 Check player movements
1362 NOTE: Actually the server should handle player physics like the
1363 client does and compare player's position to what is calculated
1364 on our side. This is required when eg. players fly due to an
1365 explosion. Altough a node-based alternative might be possible
1366 too, and much more lightweight.
1369 float player_max_speed = 0;
1371 if (m_privs.count("fast") != 0) {
1373 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1376 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1378 // Tolerance. The lag pool does this a bit.
1379 //player_max_speed *= 2.5;
1381 v3f diff = (m_base_position - m_last_good_position);
1382 float d_vert = diff.Y;
1384 float d_horiz = diff.getLength();
1385 float required_time = d_horiz / player_max_speed;
1387 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1388 required_time = d_vert / player_max_speed; // Moving upwards
1390 if (m_move_pool.grab(required_time)) {
1391 m_last_good_position = m_base_position;
1393 actionstream << "Player " << m_player->getName()
1394 << " moved too fast; resetting position"
1396 setBasePosition(m_last_good_position);
1402 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1404 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1405 toset->MinEdge += m_base_position;
1406 toset->MaxEdge += m_base_position;