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),
787 m_physics_override_speed(1),
788 m_physics_override_jump(1),
789 m_physics_override_gravity(1),
790 m_physics_override_sneak(true),
791 m_physics_override_sneak_glitch(true),
792 m_physics_override_sent(false)
794 assert(m_peer_id != 0); // pre-condition
795 m_armor_groups["fleshy"] = 100;
797 m_prop.hp_max = PLAYER_MAX_HP;
798 m_prop.physical = false;
800 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
801 // start of default appearance, this should be overwritten by LUA
802 m_prop.visual = "upright_sprite";
803 m_prop.visual_size = v2f(1, 2);
804 m_prop.textures.clear();
805 m_prop.textures.push_back("player.png");
806 m_prop.textures.push_back("player_back.png");
807 m_prop.colors.clear();
808 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
809 m_prop.spritediv = v2s16(1,1);
810 // end of default appearance
811 m_prop.is_visible = true;
812 m_prop.makes_footstep_sound = true;
813 m_hp = PLAYER_MAX_HP;
816 PlayerSAO::~PlayerSAO()
818 if(m_inventory != &m_player->inventory)
822 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
827 m_inventory = &m_player->inventory;
830 std::string PlayerSAO::getDescription()
832 return std::string("player ") + m_player->getName();
835 // Called after id has been set and has been inserted in environment
836 void PlayerSAO::addedToEnvironment(u32 dtime_s)
838 ServerActiveObject::addedToEnvironment(dtime_s);
839 ServerActiveObject::setBasePosition(m_base_position);
840 m_player->setPlayerSAO(this);
841 m_player->peer_id = m_peer_id;
842 m_last_good_position = m_base_position;
845 // Called before removing from environment
846 void PlayerSAO::removingFromEnvironment()
848 ServerActiveObject::removingFromEnvironment();
849 if (m_player->getPlayerSAO() == this) {
850 unlinkPlayerSessionAndSave();
851 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
852 it != m_attached_particle_spawners.end(); ++it) {
853 m_env->deleteParticleSpawner(*it, false);
858 bool PlayerSAO::isStaticAllowed() const
863 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
865 std::ostringstream os(std::ios::binary);
867 if(protocol_version >= 15)
869 writeU8(os, 1); // version
870 os<<serializeString(m_player->getName()); // name
871 writeU8(os, 1); // is_player
872 writeS16(os, getId()); //id
873 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
874 writeF1000(os, m_yaw);
875 writeS16(os, getHP());
877 std::ostringstream msg_os(std::ios::binary);
878 msg_os << serializeLongString(getPropertyPacket()); // message 1
879 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
880 msg_os << serializeLongString(gob_cmd_update_animation(
881 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
882 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
883 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
884 msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
885 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
887 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
888 m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
889 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
890 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
891 m_physics_override_sneak_glitch)); // 5
892 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
893 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
894 int message_count = 6 + m_bone_position.size();
895 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
896 ii != m_attachment_child_ids.end(); ++ii) {
897 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
899 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
900 obj->getClientInitializationData(protocol_version)));
904 writeU8(os, message_count);
905 os.write(msg_os.str().c_str(), msg_os.str().size());
909 writeU8(os, 0); // version
910 os<<serializeString(m_player->getName()); // name
911 writeU8(os, 1); // is_player
912 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
913 writeF1000(os, m_yaw);
914 writeS16(os, getHP());
915 writeU8(os, 2); // number of messages stuffed in here
916 os<<serializeLongString(getPropertyPacket()); // message 1
917 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
924 std::string PlayerSAO::getStaticData()
926 FATAL_ERROR("Deprecated function (?)");
930 bool PlayerSAO::isAttached()
932 if(!m_attachment_parent_id)
934 // Check if the parent still exists
935 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
941 void PlayerSAO::step(float dtime, bool send_recommended)
943 if(!m_properties_sent)
945 m_properties_sent = true;
946 std::string str = getPropertyPacket();
947 // create message and add to list
948 ActiveObjectMessage aom(getId(), true, str);
949 m_messages_out.push(aom);
952 // If attached, check that our parent is still there. If it isn't, detach.
953 if(m_attachment_parent_id && !isAttached())
955 m_attachment_parent_id = 0;
956 m_attachment_bone = "";
957 m_attachment_position = v3f(0,0,0);
958 m_attachment_rotation = v3f(0,0,0);
959 setBasePosition(m_last_good_position);
960 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
963 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
965 // Set lag pool maximums based on estimated lag
966 const float LAG_POOL_MIN = 5.0;
967 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
968 if(lag_pool_max < LAG_POOL_MIN)
969 lag_pool_max = LAG_POOL_MIN;
970 m_dig_pool.setMax(lag_pool_max);
971 m_move_pool.setMax(lag_pool_max);
973 // Increment cheat prevention timers
974 m_dig_pool.add(dtime);
975 m_move_pool.add(dtime);
976 m_time_from_last_punch += dtime;
977 m_nocheat_dig_time += dtime;
979 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
980 // If the object gets detached this comes into effect automatically from the last known origin
982 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
983 m_last_good_position = pos;
984 setBasePosition(pos);
987 if (!send_recommended)
990 // If the object is attached client-side, don't waste bandwidth sending its position to clients
991 if(m_position_not_sent && !isAttached())
993 m_position_not_sent = false;
994 float update_interval = m_env->getSendRecommendedInterval();
996 if(isAttached()) // Just in case we ever do send attachment position too
997 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
999 pos = m_base_position + v3f(0,BS*1,0);
1000 std::string str = gob_cmd_update_position(
1009 // create message and add to list
1010 ActiveObjectMessage aom(getId(), false, str);
1011 m_messages_out.push(aom);
1014 if (!m_armor_groups_sent) {
1015 m_armor_groups_sent = true;
1016 std::string str = gob_cmd_update_armor_groups(
1018 // create message and add to list
1019 ActiveObjectMessage aom(getId(), true, str);
1020 m_messages_out.push(aom);
1023 if (!m_physics_override_sent) {
1024 m_physics_override_sent = true;
1025 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1026 m_physics_override_jump, m_physics_override_gravity,
1027 m_physics_override_sneak, m_physics_override_sneak_glitch);
1028 // create message and add to list
1029 ActiveObjectMessage aom(getId(), true, str);
1030 m_messages_out.push(aom);
1033 if (!m_animation_sent) {
1034 m_animation_sent = true;
1035 std::string str = gob_cmd_update_animation(
1036 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1037 // create message and add to list
1038 ActiveObjectMessage aom(getId(), true, str);
1039 m_messages_out.push(aom);
1042 if (!m_bone_position_sent) {
1043 m_bone_position_sent = true;
1044 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1045 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1046 std::string str = gob_cmd_update_bone_position((*ii).first,
1047 (*ii).second.X, (*ii).second.Y);
1048 // create message and add to list
1049 ActiveObjectMessage aom(getId(), true, str);
1050 m_messages_out.push(aom);
1054 if (!m_attachment_sent){
1055 m_attachment_sent = true;
1056 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1057 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1058 // create message and add to list
1059 ActiveObjectMessage aom(getId(), true, str);
1060 m_messages_out.push(aom);
1064 void PlayerSAO::setBasePosition(const v3f &position)
1066 if (m_player && position != m_base_position)
1067 m_player->setDirty(true);
1069 // This needs to be ran for attachments too
1070 ServerActiveObject::setBasePosition(position);
1071 m_position_not_sent = true;
1074 void PlayerSAO::setPos(const v3f &pos)
1079 setBasePosition(pos);
1080 // Movement caused by this command is always valid
1081 m_last_good_position = pos;
1082 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1085 void PlayerSAO::moveTo(v3f pos, bool continuous)
1090 setBasePosition(pos);
1091 // Movement caused by this command is always valid
1092 m_last_good_position = pos;
1093 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1096 void PlayerSAO::setYaw(const float yaw)
1098 if (m_player && yaw != m_yaw)
1099 m_player->setDirty(true);
1101 UnitSAO::setYaw(yaw);
1104 void PlayerSAO::setFov(const float fov)
1106 if (m_player && fov != m_fov)
1107 m_player->setDirty(true);
1112 void PlayerSAO::setWantedRange(const s16 range)
1114 if (m_player && range != m_wanted_range)
1115 m_player->setDirty(true);
1117 m_wanted_range = range;
1120 void PlayerSAO::setYawAndSend(const float yaw)
1123 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1126 void PlayerSAO::setPitch(const float pitch)
1128 if (m_player && pitch != m_pitch)
1129 m_player->setDirty(true);
1134 void PlayerSAO::setPitchAndSend(const float pitch)
1137 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1140 int PlayerSAO::punch(v3f dir,
1141 const ToolCapabilities *toolcap,
1142 ServerActiveObject *puncher,
1143 float time_from_last_punch)
1145 // It's best that attachments cannot be punched
1152 // No effect if PvP disabled
1153 if (g_settings->getBool("enable_pvp") == false) {
1154 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1155 std::string str = gob_cmd_punched(0, getHP());
1156 // create message and add to list
1157 ActiveObjectMessage aom(getId(), true, str);
1158 m_messages_out.push(aom);
1163 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1164 time_from_last_punch);
1166 std::string punchername = "nil";
1169 punchername = puncher->getDescription();
1171 PlayerSAO *playersao = m_player->getPlayerSAO();
1173 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1174 puncher, time_from_last_punch, toolcap, dir,
1177 if (!damage_handled) {
1178 setHP(getHP() - hitparams.hp);
1179 } else { // override client prediction
1180 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1181 std::string str = gob_cmd_punched(0, getHP());
1182 // create message and add to list
1183 ActiveObjectMessage aom(getId(), true, str);
1184 m_messages_out.push(aom);
1189 actionstream << "Player " << m_player->getName() << " punched by "
1191 if (!damage_handled) {
1192 actionstream << ", damage " << hitparams.hp << " HP";
1194 actionstream << ", damage handled by lua";
1196 actionstream << std::endl;
1198 return hitparams.wear;
1201 void PlayerSAO::rightClick(ServerActiveObject *)
1205 s16 PlayerSAO::readDamage()
1207 s16 damage = m_damage;
1212 void PlayerSAO::setHP(s16 hp)
1216 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1219 hp = oldhp + hp_change;
1223 else if (hp > PLAYER_MAX_HP)
1226 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1233 m_damage += (oldhp - hp);
1235 // Update properties on death
1236 if ((hp == 0) != (oldhp == 0))
1237 m_properties_sent = false;
1240 void PlayerSAO::setBreath(const u16 breath)
1242 if (m_player && breath != m_breath)
1243 m_player->setDirty(true);
1248 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1250 m_armor_groups = armor_groups;
1251 m_armor_groups_sent = false;
1254 ItemGroupList PlayerSAO::getArmorGroups()
1256 return m_armor_groups;
1259 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1261 // store these so they can be updated to clients
1262 m_animation_range = frame_range;
1263 m_animation_speed = frame_speed;
1264 m_animation_blend = frame_blend;
1265 m_animation_loop = frame_loop;
1266 m_animation_sent = false;
1269 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1271 *frame_range = m_animation_range;
1272 *frame_speed = m_animation_speed;
1273 *frame_blend = m_animation_blend;
1274 *frame_loop = m_animation_loop;
1277 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1279 // store these so they can be updated to clients
1280 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1281 m_bone_position_sent = false;
1284 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1286 *position = m_bone_position[bone].X;
1287 *rotation = m_bone_position[bone].Y;
1290 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1292 // Attachments need to be handled on both the server and client.
1293 // If we just attach on the server, we can only copy the position of the parent. Attachments
1294 // are still sent to clients at an interval so players might see them lagging, plus we can't
1295 // read and attach to skeletal bones.
1296 // If we just attach on the client, the server still sees the child at its original location.
1297 // This breaks some things so we also give the server the most accurate representation
1298 // even if players only see the client changes.
1300 m_attachment_parent_id = parent_id;
1301 m_attachment_bone = bone;
1302 m_attachment_position = position;
1303 m_attachment_rotation = rotation;
1304 m_attachment_sent = false;
1307 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1310 *parent_id = m_attachment_parent_id;
1311 *bone = m_attachment_bone;
1312 *position = m_attachment_position;
1313 *rotation = m_attachment_rotation;
1316 void PlayerSAO::addAttachmentChild(int child_id)
1318 m_attachment_child_ids.insert(child_id);
1321 void PlayerSAO::removeAttachmentChild(int child_id)
1323 m_attachment_child_ids.erase(child_id);
1326 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1328 return m_attachment_child_ids;
1331 ObjectProperties* PlayerSAO::accessObjectProperties()
1336 void PlayerSAO::notifyObjectPropertiesModified()
1338 m_properties_sent = false;
1341 Inventory* PlayerSAO::getInventory()
1345 const Inventory* PlayerSAO::getInventory() const
1350 InventoryLocation PlayerSAO::getInventoryLocation() const
1352 InventoryLocation loc;
1353 loc.setPlayer(m_player->getName());
1357 std::string PlayerSAO::getWieldList() const
1362 ItemStack PlayerSAO::getWieldedItem() const
1364 const Inventory *inv = getInventory();
1366 const InventoryList *mlist = inv->getList(getWieldList());
1367 if (mlist && getWieldIndex() < (s32)mlist->getSize())
1368 ret = mlist->getItem(getWieldIndex());
1369 if (ret.name.empty()) {
1370 const InventoryList *hlist = inv->getList("hand");
1372 ret = hlist->getItem(0);
1377 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1379 Inventory *inv = getInventory();
1381 InventoryList *mlist = inv->getList(getWieldList());
1383 ItemStack olditem = mlist->getItem(getWieldIndex());
1384 if (olditem.name.empty()) {
1385 InventoryList *hlist = inv->getList("hand");
1387 hlist->changeItem(0, item);
1391 mlist->changeItem(getWieldIndex(), item);
1398 int PlayerSAO::getWieldIndex() const
1400 return m_wield_index;
1403 void PlayerSAO::setWieldIndex(int i)
1405 if(i != m_wield_index) {
1410 // Erase the peer id and make the object for removal
1411 void PlayerSAO::disconnected()
1417 void PlayerSAO::unlinkPlayerSessionAndSave()
1419 assert(m_player->getPlayerSAO() == this);
1420 m_player->peer_id = 0;
1421 m_env->savePlayer(m_player);
1422 m_player->setPlayerSAO(NULL);
1423 m_env->removePlayer(m_player);
1426 std::string PlayerSAO::getPropertyPacket()
1428 m_prop.is_visible = (true);
1429 return gob_cmd_set_properties(m_prop);
1432 bool PlayerSAO::checkMovementCheat()
1434 if (isAttached() || m_is_singleplayer ||
1435 g_settings->getBool("disable_anticheat")) {
1436 m_last_good_position = m_base_position;
1440 bool cheated = false;
1442 Check player movements
1444 NOTE: Actually the server should handle player physics like the
1445 client does and compare player's position to what is calculated
1446 on our side. This is required when eg. players fly due to an
1447 explosion. Altough a node-based alternative might be possible
1448 too, and much more lightweight.
1451 float player_max_speed = 0;
1453 if (m_privs.count("fast") != 0) {
1455 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1458 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1460 // Tolerance. The lag pool does this a bit.
1461 //player_max_speed *= 2.5;
1463 v3f diff = (m_base_position - m_last_good_position);
1464 float d_vert = diff.Y;
1466 float d_horiz = diff.getLength();
1467 float required_time = d_horiz / player_max_speed;
1469 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1470 required_time = d_vert / player_max_speed; // Moving upwards
1472 if (m_move_pool.grab(required_time)) {
1473 m_last_good_position = m_base_position;
1475 actionstream << "Player " << m_player->getName()
1476 << " moved too fast; resetting position"
1478 setBasePosition(m_last_good_position);
1484 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1486 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1487 toset->MinEdge += m_base_position;
1488 toset->MaxEdge += m_base_position;
1492 bool PlayerSAO::collideWithObjects()