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 std::string LuaEntitySAO::getStaticData() 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 std::string PlayerSAO::getStaticData() const
923 FATAL_ERROR("Deprecated function (?)");
927 void PlayerSAO::step(float dtime, bool send_recommended)
929 if (m_drowning_interval.step(dtime, 2.0)) {
931 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
932 MapNode n = m_env->getMap().getNodeNoEx(p);
933 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
934 // If node generates drown
935 if (c.drowning > 0) {
936 if (m_hp > 0 && m_breath > 0)
937 setBreath(m_breath - 1);
939 // No more breath, damage player
941 setHP(m_hp - c.drowning);
942 m_env->getGameDef()->SendPlayerHPOrDie(this);
947 if (m_breathing_interval.step(dtime, 0.5)) {
949 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
950 MapNode n = m_env->getMap().getNodeNoEx(p);
951 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
952 // If player is alive & no drowning, breath
953 if (m_hp > 0 && c.drowning == 0)
954 setBreath(m_breath + 1);
957 if (!m_properties_sent) {
958 m_properties_sent = true;
959 std::string str = getPropertyPacket();
960 // create message and add to list
961 ActiveObjectMessage aom(getId(), true, str);
962 m_messages_out.push(aom);
965 // If attached, check that our parent is still there. If it isn't, detach.
966 if(m_attachment_parent_id && !isAttached())
968 m_attachment_parent_id = 0;
969 m_attachment_bone = "";
970 m_attachment_position = v3f(0,0,0);
971 m_attachment_rotation = v3f(0,0,0);
972 setBasePosition(m_last_good_position);
973 m_env->getGameDef()->SendMovePlayer(m_peer_id);
976 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
978 // Set lag pool maximums based on estimated lag
979 const float LAG_POOL_MIN = 5.0;
980 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
981 if(lag_pool_max < LAG_POOL_MIN)
982 lag_pool_max = LAG_POOL_MIN;
983 m_dig_pool.setMax(lag_pool_max);
984 m_move_pool.setMax(lag_pool_max);
986 // Increment cheat prevention timers
987 m_dig_pool.add(dtime);
988 m_move_pool.add(dtime);
989 m_time_from_last_punch += dtime;
990 m_nocheat_dig_time += dtime;
992 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
993 // If the object gets detached this comes into effect automatically from the last known origin
995 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
996 m_last_good_position = pos;
997 setBasePosition(pos);
1000 if (!send_recommended)
1003 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1004 if(m_position_not_sent && !isAttached())
1006 m_position_not_sent = false;
1007 float update_interval = m_env->getSendRecommendedInterval();
1009 if(isAttached()) // Just in case we ever do send attachment position too
1010 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1012 pos = m_base_position + v3f(0,BS*1,0);
1013 std::string str = gob_cmd_update_position(
1022 // create message and add to list
1023 ActiveObjectMessage aom(getId(), false, str);
1024 m_messages_out.push(aom);
1027 if (!m_armor_groups_sent) {
1028 m_armor_groups_sent = true;
1029 std::string str = gob_cmd_update_armor_groups(
1031 // create message and add to list
1032 ActiveObjectMessage aom(getId(), true, str);
1033 m_messages_out.push(aom);
1036 if (!m_physics_override_sent) {
1037 m_physics_override_sent = true;
1038 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1039 m_physics_override_jump, m_physics_override_gravity,
1040 m_physics_override_sneak, m_physics_override_sneak_glitch);
1041 // create message and add to list
1042 ActiveObjectMessage aom(getId(), true, str);
1043 m_messages_out.push(aom);
1046 if (!m_animation_sent) {
1047 m_animation_sent = true;
1048 std::string str = gob_cmd_update_animation(
1049 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), true, str);
1052 m_messages_out.push(aom);
1055 if (!m_bone_position_sent) {
1056 m_bone_position_sent = true;
1057 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1058 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1059 std::string str = gob_cmd_update_bone_position((*ii).first,
1060 (*ii).second.X, (*ii).second.Y);
1061 // create message and add to list
1062 ActiveObjectMessage aom(getId(), true, str);
1063 m_messages_out.push(aom);
1067 if (!m_attachment_sent){
1068 m_attachment_sent = true;
1069 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1070 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1071 // create message and add to list
1072 ActiveObjectMessage aom(getId(), true, str);
1073 m_messages_out.push(aom);
1077 void PlayerSAO::setBasePosition(const v3f &position)
1079 if (m_player && position != m_base_position)
1080 m_player->setDirty(true);
1082 // This needs to be ran for attachments too
1083 ServerActiveObject::setBasePosition(position);
1084 m_position_not_sent = true;
1087 void PlayerSAO::setPos(const v3f &pos)
1092 setBasePosition(pos);
1093 // Movement caused by this command is always valid
1094 m_last_good_position = pos;
1095 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1098 void PlayerSAO::moveTo(v3f pos, bool continuous)
1103 setBasePosition(pos);
1104 // Movement caused by this command is always valid
1105 m_last_good_position = pos;
1106 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1109 void PlayerSAO::setYaw(const float yaw)
1111 if (m_player && yaw != m_yaw)
1112 m_player->setDirty(true);
1114 UnitSAO::setYaw(yaw);
1117 void PlayerSAO::setFov(const float fov)
1119 if (m_player && fov != m_fov)
1120 m_player->setDirty(true);
1125 void PlayerSAO::setWantedRange(const s16 range)
1127 if (m_player && range != m_wanted_range)
1128 m_player->setDirty(true);
1130 m_wanted_range = range;
1133 void PlayerSAO::setYawAndSend(const float yaw)
1136 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1139 void PlayerSAO::setPitch(const float pitch)
1141 if (m_player && pitch != m_pitch)
1142 m_player->setDirty(true);
1147 void PlayerSAO::setPitchAndSend(const float pitch)
1150 m_env->getGameDef()->SendMovePlayer(m_peer_id);
1153 int PlayerSAO::punch(v3f dir,
1154 const ToolCapabilities *toolcap,
1155 ServerActiveObject *puncher,
1156 float time_from_last_punch)
1158 // It's best that attachments cannot be punched
1165 // No effect if PvP disabled
1166 if (g_settings->getBool("enable_pvp") == false) {
1167 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1168 std::string str = gob_cmd_punched(0, getHP());
1169 // create message and add to list
1170 ActiveObjectMessage aom(getId(), true, str);
1171 m_messages_out.push(aom);
1176 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1177 time_from_last_punch);
1179 std::string punchername = "nil";
1182 punchername = puncher->getDescription();
1184 PlayerSAO *playersao = m_player->getPlayerSAO();
1186 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1187 puncher, time_from_last_punch, toolcap, dir,
1190 if (!damage_handled) {
1191 setHP(getHP() - hitparams.hp);
1192 } else { // override client prediction
1193 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1194 std::string str = gob_cmd_punched(0, getHP());
1195 // create message and add to list
1196 ActiveObjectMessage aom(getId(), true, str);
1197 m_messages_out.push(aom);
1202 actionstream << "Player " << m_player->getName() << " punched by "
1204 if (!damage_handled) {
1205 actionstream << ", damage " << hitparams.hp << " HP";
1207 actionstream << ", damage handled by lua";
1209 actionstream << std::endl;
1211 return hitparams.wear;
1214 s16 PlayerSAO::readDamage()
1216 s16 damage = m_damage;
1221 void PlayerSAO::setHP(s16 hp)
1225 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1228 hp = oldhp + hp_change;
1232 else if (hp > PLAYER_MAX_HP)
1235 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1242 m_damage += (oldhp - hp);
1244 // Update properties on death
1245 if ((hp == 0) != (oldhp == 0))
1246 m_properties_sent = false;
1249 void PlayerSAO::setBreath(const u16 breath, bool send)
1251 if (m_player && breath != m_breath)
1252 m_player->setDirty(true);
1254 m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1257 m_env->getGameDef()->SendPlayerBreath(this);
1260 Inventory* PlayerSAO::getInventory()
1264 const Inventory* PlayerSAO::getInventory() const
1269 InventoryLocation PlayerSAO::getInventoryLocation() const
1271 InventoryLocation loc;
1272 loc.setPlayer(m_player->getName());
1276 std::string PlayerSAO::getWieldList() const
1281 ItemStack PlayerSAO::getWieldedItem() const
1283 const Inventory *inv = getInventory();
1285 const InventoryList *mlist = inv->getList(getWieldList());
1286 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1287 ret = mlist->getItem(getWieldIndex());
1288 if (ret.name.empty()) {
1289 const InventoryList *hlist = inv->getList("hand");
1291 ret = hlist->getItem(0);
1296 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1298 Inventory *inv = getInventory();
1300 InventoryList *mlist = inv->getList(getWieldList());
1302 ItemStack olditem = mlist->getItem(getWieldIndex());
1303 if (olditem.name.empty()) {
1304 InventoryList *hlist = inv->getList("hand");
1306 hlist->changeItem(0, item);
1310 mlist->changeItem(getWieldIndex(), item);
1317 int PlayerSAO::getWieldIndex() const
1319 return m_wield_index;
1322 void PlayerSAO::setWieldIndex(int i)
1324 if(i != m_wield_index) {
1329 // Erase the peer id and make the object for removal
1330 void PlayerSAO::disconnected()
1336 void PlayerSAO::unlinkPlayerSessionAndSave()
1338 assert(m_player->getPlayerSAO() == this);
1339 m_player->peer_id = 0;
1340 m_env->savePlayer(m_player);
1341 m_player->setPlayerSAO(NULL);
1342 m_env->removePlayer(m_player);
1345 std::string PlayerSAO::getPropertyPacket()
1347 m_prop.is_visible = (true);
1348 return gob_cmd_set_properties(m_prop);
1351 bool PlayerSAO::checkMovementCheat()
1353 if (isAttached() || m_is_singleplayer ||
1354 g_settings->getBool("disable_anticheat")) {
1355 m_last_good_position = m_base_position;
1359 bool cheated = false;
1361 Check player movements
1363 NOTE: Actually the server should handle player physics like the
1364 client does and compare player's position to what is calculated
1365 on our side. This is required when eg. players fly due to an
1366 explosion. Altough a node-based alternative might be possible
1367 too, and much more lightweight.
1370 float player_max_speed = 0;
1372 if (m_privs.count("fast") != 0) {
1374 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1377 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1379 // Tolerance. The lag pool does this a bit.
1380 //player_max_speed *= 2.5;
1382 v3f diff = (m_base_position - m_last_good_position);
1383 float d_vert = diff.Y;
1385 float d_horiz = diff.getLength();
1386 float required_time = d_horiz / player_max_speed;
1388 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1389 required_time = d_vert / player_max_speed; // Moving upwards
1391 if (m_move_pool.grab(required_time)) {
1392 m_last_good_position = m_base_position;
1394 actionstream << "Player " << m_player->getName()
1395 << " moved too fast; resetting position"
1397 setBasePosition(m_last_good_position);
1403 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1405 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1406 toset->MinEdge += m_base_position;
1407 toset->MaxEdge += m_base_position;