3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
29 #include "remoteplayer.h"
31 #include "scripting_game.h"
32 #include "genericobject.h"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class TestSAO : public ServerActiveObject
44 TestSAO(ServerEnvironment *env, v3f pos):
45 ServerActiveObject(env, pos),
49 ServerActiveObject::registerType(getType(), create);
51 ActiveObjectType getType() const
52 { return ACTIVEOBJECT_TYPE_TEST; }
54 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
55 const std::string &data)
57 return new TestSAO(env, pos);
60 void step(float dtime, bool send_recommended)
69 m_base_position.Y += dtime * BS * 2;
70 if(m_base_position.Y > 8*BS)
71 m_base_position.Y = 2*BS;
73 if(send_recommended == false)
83 data += itos(0); // 0 = position
85 data += itos(m_base_position.X);
87 data += itos(m_base_position.Y);
89 data += itos(m_base_position.Z);
91 ActiveObjectMessage aom(getId(), false, data);
92 m_messages_out.push(aom);
96 bool getCollisionBox(aabb3f *toset) {
100 bool collideWithObjects() {
109 // Prototype (registers item for deserialization)
110 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
116 // Prototype (registers item for deserialization)
117 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
119 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
120 const std::string &name, const std::string &state):
126 m_acceleration(0,0,0),
127 m_properties_sent(true),
129 m_last_sent_position(0,0,0),
130 m_last_sent_velocity(0,0,0),
131 m_last_sent_position_timer(0),
132 m_last_sent_move_precision(0),
133 m_armor_groups_sent(false),
134 m_animation_speed(0),
135 m_animation_blend(0),
136 m_animation_loop(true),
137 m_animation_sent(false),
138 m_bone_position_sent(false),
139 m_attachment_parent_id(0),
140 m_attachment_sent(false)
142 // Only register type if no environment supplied
144 ServerActiveObject::registerType(getType(), create);
148 // Initialize something to armor groups
149 m_armor_groups["fleshy"] = 100;
152 LuaEntitySAO::~LuaEntitySAO()
155 m_env->getScriptIface()->luaentity_Remove(m_id);
158 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
159 it != m_attached_particle_spawners.end(); ++it) {
160 m_env->deleteParticleSpawner(*it, false);
164 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
166 ServerActiveObject::addedToEnvironment(dtime_s);
168 // Create entity from name
169 m_registered = m_env->getScriptIface()->
170 luaentity_Add(m_id, m_init_name.c_str());
174 m_env->getScriptIface()->
175 luaentity_GetProperties(m_id, &m_prop);
176 // Initialize HP from properties
177 m_hp = m_prop.hp_max;
178 // Activate entity, supplying serialized state
179 m_env->getScriptIface()->
180 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
182 m_prop.infotext = m_init_name;
186 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
187 const std::string &data)
195 std::istringstream is(data, std::ios::binary);
197 u8 version = readU8(is);
198 // check if version is supported
200 name = deSerializeString(is);
201 state = deSerializeLongString(is);
203 else if(version == 1){
204 name = deSerializeString(is);
205 state = deSerializeLongString(is);
207 velocity = readV3F1000(is);
212 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
213 <<state<<"\")"<<std::endl;
214 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
216 sao->m_velocity = velocity;
221 bool LuaEntitySAO::isAttached()
223 if(!m_attachment_parent_id)
225 // Check if the parent still exists
226 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
232 void LuaEntitySAO::step(float dtime, bool send_recommended)
234 if(!m_properties_sent)
236 m_properties_sent = true;
237 std::string str = getPropertyPacket();
238 // create message and add to list
239 ActiveObjectMessage aom(getId(), true, str);
240 m_messages_out.push(aom);
243 // If attached, check that our parent is still there. If it isn't, detach.
244 if(m_attachment_parent_id && !isAttached())
246 m_attachment_parent_id = 0;
247 m_attachment_bone = "";
248 m_attachment_position = v3f(0,0,0);
249 m_attachment_rotation = v3f(0,0,0);
250 sendPosition(false, true);
253 m_last_sent_position_timer += dtime;
255 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
256 // If the object gets detached this comes into effect automatically from the last known origin
259 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
260 m_base_position = pos;
261 m_velocity = v3f(0,0,0);
262 m_acceleration = v3f(0,0,0);
267 aabb3f box = m_prop.collisionbox;
270 collisionMoveResult moveresult;
271 f32 pos_max_d = BS*0.25; // Distance per iteration
272 v3f p_pos = m_base_position;
273 v3f p_velocity = m_velocity;
274 v3f p_acceleration = m_acceleration;
275 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
276 pos_max_d, box, m_prop.stepheight, dtime,
277 &p_pos, &p_velocity, p_acceleration,
278 this, m_prop.collideWithObjects);
281 m_base_position = p_pos;
282 m_velocity = p_velocity;
283 m_acceleration = p_acceleration;
285 m_base_position += dtime * m_velocity + 0.5 * dtime
286 * dtime * m_acceleration;
287 m_velocity += dtime * m_acceleration;
290 if((m_prop.automatic_face_movement_dir) &&
291 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
293 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
294 + m_prop.automatic_face_movement_dir_offset;
295 float max_rotation_delta =
296 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
298 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
299 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
301 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
309 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
312 if(send_recommended == false)
317 // TODO: force send when acceleration changes enough?
318 float minchange = 0.2*BS;
319 if(m_last_sent_position_timer > 1.0){
321 } else if(m_last_sent_position_timer > 0.2){
324 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
325 move_d += m_last_sent_move_precision;
326 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
327 if(move_d > minchange || vel_d > minchange ||
328 fabs(m_yaw - m_last_sent_yaw) > 1.0){
329 sendPosition(true, false);
333 if(m_armor_groups_sent == false){
334 m_armor_groups_sent = true;
335 std::string str = gob_cmd_update_armor_groups(
337 // create message and add to list
338 ActiveObjectMessage aom(getId(), true, str);
339 m_messages_out.push(aom);
342 if(m_animation_sent == false){
343 m_animation_sent = true;
344 std::string str = gob_cmd_update_animation(
345 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
346 // create message and add to list
347 ActiveObjectMessage aom(getId(), true, str);
348 m_messages_out.push(aom);
351 if(m_bone_position_sent == false){
352 m_bone_position_sent = true;
353 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
354 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
355 std::string str = gob_cmd_update_bone_position((*ii).first,
356 (*ii).second.X, (*ii).second.Y);
357 // create message and add to list
358 ActiveObjectMessage aom(getId(), true, str);
359 m_messages_out.push(aom);
363 if(m_attachment_sent == false){
364 m_attachment_sent = true;
365 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
366 // create message and add to list
367 ActiveObjectMessage aom(getId(), true, str);
368 m_messages_out.push(aom);
372 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
374 std::ostringstream os(std::ios::binary);
376 if(protocol_version >= 14)
378 writeU8(os, 1); // version
379 os<<serializeString(""); // name
380 writeU8(os, 0); // is_player
381 writeS16(os, getId()); //id
382 writeV3F1000(os, m_base_position);
383 writeF1000(os, m_yaw);
386 std::ostringstream msg_os(std::ios::binary);
387 msg_os << serializeLongString(getPropertyPacket()); // message 1
388 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
389 msg_os << serializeLongString(gob_cmd_update_animation(
390 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
391 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
392 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
393 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
394 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
396 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
397 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
398 int message_count = 4 + m_bone_position.size();
399 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
400 (ii != m_attachment_child_ids.end()); ++ii) {
401 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
403 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
404 obj->getClientInitializationData(protocol_version)));
408 writeU8(os, message_count);
409 os.write(msg_os.str().c_str(), msg_os.str().size());
413 writeU8(os, 0); // version
414 os<<serializeString(""); // name
415 writeU8(os, 0); // is_player
416 writeV3F1000(os, m_base_position);
417 writeF1000(os, m_yaw);
419 writeU8(os, 2); // number of messages stuffed in here
420 os<<serializeLongString(getPropertyPacket()); // message 1
421 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
428 std::string LuaEntitySAO::getStaticData()
430 verbosestream<<FUNCTION_NAME<<std::endl;
431 std::ostringstream os(std::ios::binary);
435 os<<serializeString(m_init_name);
438 std::string state = m_env->getScriptIface()->
439 luaentity_GetStaticdata(m_id);
440 os<<serializeLongString(state);
442 os<<serializeLongString(m_init_state);
447 writeV3F1000(os, m_velocity);
449 writeF1000(os, m_yaw);
453 int LuaEntitySAO::punch(v3f dir,
454 const ToolCapabilities *toolcap,
455 ServerActiveObject *puncher,
456 float time_from_last_punch)
459 // Delete unknown LuaEntities when punched
464 // It's best that attachments cannot be punched
468 ItemStack *punchitem = NULL;
469 ItemStack punchitem_static;
471 punchitem_static = puncher->getWieldedItem();
472 punchitem = &punchitem_static;
475 PunchDamageResult result = getPunchDamage(
479 time_from_last_punch);
481 if (result.did_punch) {
482 setHP(getHP() - result.damage);
484 if (result.damage > 0) {
485 std::string punchername = puncher ? puncher->getDescription() : "nil";
487 actionstream << getDescription() << " punched by "
488 << punchername << ", damage " << result.damage
489 << " hp, health now " << getHP() << " hp" << std::endl;
492 std::string str = gob_cmd_punched(result.damage, getHP());
493 // create message and add to list
494 ActiveObjectMessage aom(getId(), true, str);
495 m_messages_out.push(aom);
501 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
502 time_from_last_punch, toolcap, dir);
507 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
511 // It's best that attachments cannot be clicked
514 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
517 void LuaEntitySAO::setPos(const v3f &pos)
521 m_base_position = pos;
522 sendPosition(false, true);
525 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
529 m_base_position = pos;
531 sendPosition(true, true);
534 float LuaEntitySAO::getMinimumSavedMovement()
539 std::string LuaEntitySAO::getDescription()
541 std::ostringstream os(std::ios::binary);
542 os<<"LuaEntitySAO at (";
543 os<<(m_base_position.X/BS)<<",";
544 os<<(m_base_position.Y/BS)<<",";
545 os<<(m_base_position.Z/BS);
550 void LuaEntitySAO::setHP(s16 hp)
556 s16 LuaEntitySAO::getHP() const
561 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
563 m_armor_groups = armor_groups;
564 m_armor_groups_sent = false;
567 ItemGroupList LuaEntitySAO::getArmorGroups()
569 return m_armor_groups;
572 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
574 m_animation_range = frame_range;
575 m_animation_speed = frame_speed;
576 m_animation_blend = frame_blend;
577 m_animation_loop = frame_loop;
578 m_animation_sent = false;
581 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
583 *frame_range = m_animation_range;
584 *frame_speed = m_animation_speed;
585 *frame_blend = m_animation_blend;
586 *frame_loop = m_animation_loop;
589 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
591 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
592 m_bone_position_sent = false;
595 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
597 *position = m_bone_position[bone].X;
598 *rotation = m_bone_position[bone].Y;
601 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
603 // Attachments need to be handled on both the server and client.
604 // If we just attach on the server, we can only copy the position of the parent. Attachments
605 // are still sent to clients at an interval so players might see them lagging, plus we can't
606 // read and attach to skeletal bones.
607 // If we just attach on the client, the server still sees the child at its original location.
608 // This breaks some things so we also give the server the most accurate representation
609 // even if players only see the client changes.
611 m_attachment_parent_id = parent_id;
612 m_attachment_bone = bone;
613 m_attachment_position = position;
614 m_attachment_rotation = rotation;
615 m_attachment_sent = false;
618 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
621 *parent_id = m_attachment_parent_id;
622 *bone = m_attachment_bone;
623 *position = m_attachment_position;
624 *rotation = m_attachment_rotation;
627 void LuaEntitySAO::addAttachmentChild(int child_id)
629 m_attachment_child_ids.insert(child_id);
632 void LuaEntitySAO::removeAttachmentChild(int child_id)
634 m_attachment_child_ids.erase(child_id);
637 UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
639 return m_attachment_child_ids;
642 ObjectProperties* LuaEntitySAO::accessObjectProperties()
647 void LuaEntitySAO::notifyObjectPropertiesModified()
649 m_properties_sent = false;
652 void LuaEntitySAO::setVelocity(v3f velocity)
654 m_velocity = velocity;
657 v3f LuaEntitySAO::getVelocity()
662 void LuaEntitySAO::setAcceleration(v3f acceleration)
664 m_acceleration = acceleration;
667 v3f LuaEntitySAO::getAcceleration()
669 return m_acceleration;
672 void LuaEntitySAO::setTextureMod(const std::string &mod)
674 std::string str = gob_cmd_set_texture_mod(mod);
675 // create message and add to list
676 ActiveObjectMessage aom(getId(), true, str);
677 m_messages_out.push(aom);
680 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
681 bool select_horiz_by_yawpitch)
683 std::string str = gob_cmd_set_sprite(
687 select_horiz_by_yawpitch
689 // create message and add to list
690 ActiveObjectMessage aom(getId(), true, str);
691 m_messages_out.push(aom);
694 std::string LuaEntitySAO::getName()
699 std::string LuaEntitySAO::getPropertyPacket()
701 return gob_cmd_set_properties(m_prop);
704 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
706 // If the object is attached client-side, don't waste bandwidth sending its position to clients
710 m_last_sent_move_precision = m_base_position.getDistanceFrom(
711 m_last_sent_position);
712 m_last_sent_position_timer = 0;
713 m_last_sent_yaw = m_yaw;
714 m_last_sent_position = m_base_position;
715 m_last_sent_velocity = m_velocity;
716 //m_last_sent_acceleration = m_acceleration;
718 float update_interval = m_env->getSendRecommendedInterval();
720 std::string str = gob_cmd_update_position(
729 // create message and add to list
730 ActiveObjectMessage aom(getId(), false, str);
731 m_messages_out.push(aom);
734 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
737 //update collision box
738 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
739 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
741 toset->MinEdge += m_base_position;
742 toset->MaxEdge += m_base_position;
750 bool LuaEntitySAO::collideWithObjects(){
751 return m_prop.collideWithObjects;
758 // No prototype, PlayerSAO does not need to be deserialized
760 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
761 UnitSAO(env_, v3f(0,0,0)),
766 m_last_good_position(0,0,0),
767 m_time_from_last_punch(0),
768 m_nocheat_dig_pos(32767, 32767, 32767),
769 m_nocheat_dig_time(0),
771 m_position_not_sent(false),
772 m_armor_groups_sent(false),
773 m_properties_sent(true),
774 m_is_singleplayer(is_singleplayer),
775 m_animation_speed(0),
776 m_animation_blend(0),
777 m_animation_loop(true),
778 m_animation_sent(false),
779 m_bone_position_sent(false),
780 m_attachment_parent_id(0),
781 m_attachment_sent(false),
782 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
793 m_armor_groups["fleshy"] = 100;
795 m_prop.hp_max = PLAYER_MAX_HP;
796 m_prop.physical = false;
798 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
799 // start of default appearance, this should be overwritten by LUA
800 m_prop.visual = "upright_sprite";
801 m_prop.visual_size = v2f(1, 2);
802 m_prop.textures.clear();
803 m_prop.textures.push_back("player.png");
804 m_prop.textures.push_back("player_back.png");
805 m_prop.colors.clear();
806 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
807 m_prop.spritediv = v2s16(1,1);
808 // end of default appearance
809 m_prop.is_visible = true;
810 m_prop.makes_footstep_sound = true;
811 m_hp = PLAYER_MAX_HP;
814 PlayerSAO::~PlayerSAO()
816 if(m_inventory != &m_player->inventory)
820 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
825 m_inventory = &m_player->inventory;
828 std::string PlayerSAO::getDescription()
830 return std::string("player ") + m_player->getName();
833 // Called after id has been set and has been inserted in environment
834 void PlayerSAO::addedToEnvironment(u32 dtime_s)
836 ServerActiveObject::addedToEnvironment(dtime_s);
837 ServerActiveObject::setBasePosition(m_base_position);
838 m_player->setPlayerSAO(this);
839 m_player->peer_id = m_peer_id;
840 m_last_good_position = m_base_position;
843 // Called before removing from environment
844 void PlayerSAO::removingFromEnvironment()
846 ServerActiveObject::removingFromEnvironment();
847 if (m_player->getPlayerSAO() == this) {
848 unlinkPlayerSessionAndSave();
849 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
850 it != m_attached_particle_spawners.end(); ++it) {
851 m_env->deleteParticleSpawner(*it, false);
856 bool PlayerSAO::isStaticAllowed() const
861 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
863 std::ostringstream os(std::ios::binary);
865 if(protocol_version >= 15)
867 writeU8(os, 1); // version
868 os<<serializeString(m_player->getName()); // name
869 writeU8(os, 1); // is_player
870 writeS16(os, getId()); //id
871 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
872 writeF1000(os, m_yaw);
873 writeS16(os, getHP());
875 std::ostringstream msg_os(std::ios::binary);
876 msg_os << serializeLongString(getPropertyPacket()); // message 1
877 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
878 msg_os << serializeLongString(gob_cmd_update_animation(
879 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
880 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
881 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
882 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
883 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
885 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
886 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
887 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
888 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
889 m_physics_override_sneak_glitch)); // 5
890 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
891 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
892 int message_count = 6 + m_bone_position.size();
893 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
894 ii != m_attachment_child_ids.end(); ++ii) {
895 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
897 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
898 obj->getClientInitializationData(protocol_version)));
902 writeU8(os, message_count);
903 os.write(msg_os.str().c_str(), msg_os.str().size());
907 writeU8(os, 0); // version
908 os<<serializeString(m_player->getName()); // name
909 writeU8(os, 1); // is_player
910 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
911 writeF1000(os, m_yaw);
912 writeS16(os, getHP());
913 writeU8(os, 2); // number of messages stuffed in here
914 os<<serializeLongString(getPropertyPacket()); // message 1
915 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
922 std::string PlayerSAO::getStaticData()
924 FATAL_ERROR("Deprecated function (?)");
928 bool PlayerSAO::isAttached()
930 if(!m_attachment_parent_id)
932 // Check if the parent still exists
933 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
939 void PlayerSAO::step(float dtime, bool send_recommended)
941 if(!m_properties_sent)
943 m_properties_sent = true;
944 std::string str = getPropertyPacket();
945 // create message and add to list
946 ActiveObjectMessage aom(getId(), true, str);
947 m_messages_out.push(aom);
950 // If attached, check that our parent is still there. If it isn't, detach.
951 if(m_attachment_parent_id && !isAttached())
953 m_attachment_parent_id = 0;
954 m_attachment_bone = "";
955 m_attachment_position = v3f(0,0,0);
956 m_attachment_rotation = v3f(0,0,0);
957 setBasePosition(m_last_good_position);
958 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
961 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
963 // Set lag pool maximums based on estimated lag
964 const float LAG_POOL_MIN = 5.0;
965 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
966 if(lag_pool_max < LAG_POOL_MIN)
967 lag_pool_max = LAG_POOL_MIN;
968 m_dig_pool.setMax(lag_pool_max);
969 m_move_pool.setMax(lag_pool_max);
971 // Increment cheat prevention timers
972 m_dig_pool.add(dtime);
973 m_move_pool.add(dtime);
974 m_time_from_last_punch += dtime;
975 m_nocheat_dig_time += dtime;
977 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
978 // If the object gets detached this comes into effect automatically from the last known origin
980 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
981 m_last_good_position = pos;
982 setBasePosition(pos);
985 if (!send_recommended)
988 // If the object is attached client-side, don't waste bandwidth sending its position to clients
989 if(m_position_not_sent && !isAttached())
991 m_position_not_sent = false;
992 float update_interval = m_env->getSendRecommendedInterval();
994 if(isAttached()) // Just in case we ever do send attachment position too
995 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
997 pos = m_base_position + v3f(0,BS*1,0);
998 std::string str = gob_cmd_update_position(
1007 // create message and add to list
1008 ActiveObjectMessage aom(getId(), false, str);
1009 m_messages_out.push(aom);
1012 if (!m_armor_groups_sent) {
1013 m_armor_groups_sent = true;
1014 std::string str = gob_cmd_update_armor_groups(
1016 // create message and add to list
1017 ActiveObjectMessage aom(getId(), true, str);
1018 m_messages_out.push(aom);
1021 if (!m_physics_override_sent) {
1022 m_physics_override_sent = true;
1023 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1024 m_physics_override_jump, m_physics_override_gravity,
1025 m_physics_override_sneak, m_physics_override_sneak_glitch);
1026 // create message and add to list
1027 ActiveObjectMessage aom(getId(), true, str);
1028 m_messages_out.push(aom);
1031 if (!m_animation_sent) {
1032 m_animation_sent = true;
1033 std::string str = gob_cmd_update_animation(
1034 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1035 // create message and add to list
1036 ActiveObjectMessage aom(getId(), true, str);
1037 m_messages_out.push(aom);
1040 if (!m_bone_position_sent) {
1041 m_bone_position_sent = true;
1042 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1043 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1044 std::string str = gob_cmd_update_bone_position((*ii).first,
1045 (*ii).second.X, (*ii).second.Y);
1046 // create message and add to list
1047 ActiveObjectMessage aom(getId(), true, str);
1048 m_messages_out.push(aom);
1052 if (!m_attachment_sent){
1053 m_attachment_sent = true;
1054 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1055 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1056 // create message and add to list
1057 ActiveObjectMessage aom(getId(), true, str);
1058 m_messages_out.push(aom);
1062 void PlayerSAO::setBasePosition(const v3f &position)
1064 if (m_player && position != m_base_position)
1065 m_player->setDirty(true);
1067 // This needs to be ran for attachments too
1068 ServerActiveObject::setBasePosition(position);
1069 m_position_not_sent = true;
1072 void PlayerSAO::setPos(const v3f &pos)
1077 setBasePosition(pos);
1078 // Movement caused by this command is always valid
1079 m_last_good_position = pos;
1080 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1083 void PlayerSAO::moveTo(v3f pos, bool continuous)
1088 setBasePosition(pos);
1089 // Movement caused by this command is always valid
1090 m_last_good_position = pos;
1091 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1094 void PlayerSAO::setYaw(const float yaw)
1096 if (m_player && yaw != m_yaw)
1097 m_player->setDirty(true);
1099 UnitSAO::setYaw(yaw);
1102 void PlayerSAO::setYawAndSend(const float yaw)
1105 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1108 void PlayerSAO::setPitch(const float pitch)
1110 if (m_player && pitch != m_pitch)
1111 m_player->setDirty(true);
1116 void PlayerSAO::setPitchAndSend(const float pitch)
1119 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1122 int PlayerSAO::punch(v3f dir,
1123 const ToolCapabilities *toolcap,
1124 ServerActiveObject *puncher,
1125 float time_from_last_punch)
1127 // It's best that attachments cannot be punched
1134 // No effect if PvP disabled
1135 if (g_settings->getBool("enable_pvp") == false) {
1136 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1137 std::string str = gob_cmd_punched(0, getHP());
1138 // create message and add to list
1139 ActiveObjectMessage aom(getId(), true, str);
1140 m_messages_out.push(aom);
1145 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1146 time_from_last_punch);
1148 std::string punchername = "nil";
1151 punchername = puncher->getDescription();
1153 PlayerSAO *playersao = m_player->getPlayerSAO();
1155 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1156 puncher, time_from_last_punch, toolcap, dir,
1159 if (!damage_handled) {
1160 setHP(getHP() - hitparams.hp);
1161 } else { // override client prediction
1162 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1163 std::string str = gob_cmd_punched(0, getHP());
1164 // create message and add to list
1165 ActiveObjectMessage aom(getId(), true, str);
1166 m_messages_out.push(aom);
1171 actionstream << "Player " << m_player->getName() << " punched by "
1173 if (!damage_handled) {
1174 actionstream << ", damage " << hitparams.hp << " HP";
1176 actionstream << ", damage handled by lua";
1178 actionstream << std::endl;
1180 return hitparams.wear;
1183 void PlayerSAO::rightClick(ServerActiveObject *)
1187 s16 PlayerSAO::readDamage()
1189 s16 damage = m_damage;
1194 void PlayerSAO::setHP(s16 hp)
1198 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1201 hp = oldhp + hp_change;
1205 else if (hp > PLAYER_MAX_HP)
1208 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1215 m_damage += (oldhp - hp);
1217 // Update properties on death
1218 if ((hp == 0) != (oldhp == 0))
1219 m_properties_sent = false;
1222 void PlayerSAO::setBreath(const u16 breath)
1224 if (m_player && breath != m_breath)
1225 m_player->setDirty(true);
1230 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1232 m_armor_groups = armor_groups;
1233 m_armor_groups_sent = false;
1236 ItemGroupList PlayerSAO::getArmorGroups()
1238 return m_armor_groups;
1241 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1243 // store these so they can be updated to clients
1244 m_animation_range = frame_range;
1245 m_animation_speed = frame_speed;
1246 m_animation_blend = frame_blend;
1247 m_animation_loop = frame_loop;
1248 m_animation_sent = false;
1251 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1253 *frame_range = m_animation_range;
1254 *frame_speed = m_animation_speed;
1255 *frame_blend = m_animation_blend;
1256 *frame_loop = m_animation_loop;
1259 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1261 // store these so they can be updated to clients
1262 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1263 m_bone_position_sent = false;
1266 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1268 *position = m_bone_position[bone].X;
1269 *rotation = m_bone_position[bone].Y;
1272 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1274 // Attachments need to be handled on both the server and client.
1275 // If we just attach on the server, we can only copy the position of the parent. Attachments
1276 // are still sent to clients at an interval so players might see them lagging, plus we can't
1277 // read and attach to skeletal bones.
1278 // If we just attach on the client, the server still sees the child at its original location.
1279 // This breaks some things so we also give the server the most accurate representation
1280 // even if players only see the client changes.
1282 m_attachment_parent_id = parent_id;
1283 m_attachment_bone = bone;
1284 m_attachment_position = position;
1285 m_attachment_rotation = rotation;
1286 m_attachment_sent = false;
1289 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1292 *parent_id = m_attachment_parent_id;
1293 *bone = m_attachment_bone;
1294 *position = m_attachment_position;
1295 *rotation = m_attachment_rotation;
1298 void PlayerSAO::addAttachmentChild(int child_id)
1300 m_attachment_child_ids.insert(child_id);
1303 void PlayerSAO::removeAttachmentChild(int child_id)
1305 m_attachment_child_ids.erase(child_id);
1308 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1310 return m_attachment_child_ids;
1313 ObjectProperties* PlayerSAO::accessObjectProperties()
1318 void PlayerSAO::notifyObjectPropertiesModified()
1320 m_properties_sent = false;
1323 Inventory* PlayerSAO::getInventory()
1327 const Inventory* PlayerSAO::getInventory() const
1332 InventoryLocation PlayerSAO::getInventoryLocation() const
1334 InventoryLocation loc;
1335 loc.setPlayer(m_player->getName());
1339 std::string PlayerSAO::getWieldList() const
1344 int PlayerSAO::getWieldIndex() const
1346 return m_wield_index;
1349 void PlayerSAO::setWieldIndex(int i)
1351 if(i != m_wield_index) {
1356 // Erase the peer id and make the object for removal
1357 void PlayerSAO::disconnected()
1363 void PlayerSAO::unlinkPlayerSessionAndSave()
1365 assert(m_player->getPlayerSAO() == this);
1366 m_player->peer_id = 0;
1367 m_env->savePlayer(m_player);
1368 m_player->setPlayerSAO(NULL);
1369 m_env->removePlayer(m_player);
1372 std::string PlayerSAO::getPropertyPacket()
1374 m_prop.is_visible = (true);
1375 return gob_cmd_set_properties(m_prop);
1378 bool PlayerSAO::checkMovementCheat()
1380 if (isAttached() || m_is_singleplayer ||
1381 g_settings->getBool("disable_anticheat")) {
1382 m_last_good_position = m_base_position;
1386 bool cheated = false;
1388 Check player movements
1390 NOTE: Actually the server should handle player physics like the
1391 client does and compare player's position to what is calculated
1392 on our side. This is required when eg. players fly due to an
1393 explosion. Altough a node-based alternative might be possible
1394 too, and much more lightweight.
1397 float player_max_speed = 0;
1399 if (m_privs.count("fast") != 0) {
1401 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1404 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1406 // Tolerance. The lag pool does this a bit.
1407 //player_max_speed *= 2.5;
1409 v3f diff = (m_base_position - m_last_good_position);
1410 float d_vert = diff.Y;
1412 float d_horiz = diff.getLength();
1413 float required_time = d_horiz / player_max_speed;
1415 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1416 required_time = d_vert / player_max_speed; // Moving upwards
1418 if (m_move_pool.grab(required_time)) {
1419 m_last_good_position = m_base_position;
1421 actionstream << "Player " << m_player->getName()
1422 << " moved too fast; resetting position"
1424 setBasePosition(m_last_good_position);
1430 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1432 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1433 toset->MinEdge += m_base_position;
1434 toset->MaxEdge += m_base_position;
1438 bool PlayerSAO::collideWithObjects()