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 writeU8(os, 4 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
387 os<<serializeLongString(getPropertyPacket()); // message 1
388 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
389 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 os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
394 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
396 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
397 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
398 (ii != m_attachment_child_ids.end()); ++ii) {
399 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
400 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
406 writeU8(os, 0); // version
407 os<<serializeString(""); // name
408 writeU8(os, 0); // is_player
409 writeV3F1000(os, m_base_position);
410 writeF1000(os, m_yaw);
412 writeU8(os, 2); // number of messages stuffed in here
413 os<<serializeLongString(getPropertyPacket()); // message 1
414 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
421 std::string LuaEntitySAO::getStaticData()
423 verbosestream<<FUNCTION_NAME<<std::endl;
424 std::ostringstream os(std::ios::binary);
428 os<<serializeString(m_init_name);
431 std::string state = m_env->getScriptIface()->
432 luaentity_GetStaticdata(m_id);
433 os<<serializeLongString(state);
435 os<<serializeLongString(m_init_state);
440 writeV3F1000(os, m_velocity);
442 writeF1000(os, m_yaw);
446 int LuaEntitySAO::punch(v3f dir,
447 const ToolCapabilities *toolcap,
448 ServerActiveObject *puncher,
449 float time_from_last_punch)
452 // Delete unknown LuaEntities when punched
457 // It's best that attachments cannot be punched
461 ItemStack *punchitem = NULL;
462 ItemStack punchitem_static;
464 punchitem_static = puncher->getWieldedItem();
465 punchitem = &punchitem_static;
468 PunchDamageResult result = getPunchDamage(
472 time_from_last_punch);
474 if (result.did_punch) {
475 setHP(getHP() - result.damage);
477 if (result.damage > 0) {
478 std::string punchername = puncher ? puncher->getDescription() : "nil";
480 actionstream << getDescription() << " punched by "
481 << punchername << ", damage " << result.damage
482 << " hp, health now " << getHP() << " hp" << std::endl;
485 std::string str = gob_cmd_punched(result.damage, getHP());
486 // create message and add to list
487 ActiveObjectMessage aom(getId(), true, str);
488 m_messages_out.push(aom);
494 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
495 time_from_last_punch, toolcap, dir);
500 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
504 // It's best that attachments cannot be clicked
507 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
510 void LuaEntitySAO::setPos(const v3f &pos)
514 m_base_position = pos;
515 sendPosition(false, true);
518 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
522 m_base_position = pos;
524 sendPosition(true, true);
527 float LuaEntitySAO::getMinimumSavedMovement()
532 std::string LuaEntitySAO::getDescription()
534 std::ostringstream os(std::ios::binary);
535 os<<"LuaEntitySAO at (";
536 os<<(m_base_position.X/BS)<<",";
537 os<<(m_base_position.Y/BS)<<",";
538 os<<(m_base_position.Z/BS);
543 void LuaEntitySAO::setHP(s16 hp)
549 s16 LuaEntitySAO::getHP() const
554 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
556 m_armor_groups = armor_groups;
557 m_armor_groups_sent = false;
560 ItemGroupList LuaEntitySAO::getArmorGroups()
562 return m_armor_groups;
565 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
567 m_animation_range = frame_range;
568 m_animation_speed = frame_speed;
569 m_animation_blend = frame_blend;
570 m_animation_loop = frame_loop;
571 m_animation_sent = false;
574 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
576 *frame_range = m_animation_range;
577 *frame_speed = m_animation_speed;
578 *frame_blend = m_animation_blend;
579 *frame_loop = m_animation_loop;
582 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
584 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
585 m_bone_position_sent = false;
588 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
590 *position = m_bone_position[bone].X;
591 *rotation = m_bone_position[bone].Y;
594 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
596 // Attachments need to be handled on both the server and client.
597 // If we just attach on the server, we can only copy the position of the parent. Attachments
598 // are still sent to clients at an interval so players might see them lagging, plus we can't
599 // read and attach to skeletal bones.
600 // If we just attach on the client, the server still sees the child at its original location.
601 // This breaks some things so we also give the server the most accurate representation
602 // even if players only see the client changes.
604 m_attachment_parent_id = parent_id;
605 m_attachment_bone = bone;
606 m_attachment_position = position;
607 m_attachment_rotation = rotation;
608 m_attachment_sent = false;
611 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
614 *parent_id = m_attachment_parent_id;
615 *bone = m_attachment_bone;
616 *position = m_attachment_position;
617 *rotation = m_attachment_rotation;
620 void LuaEntitySAO::addAttachmentChild(int child_id)
622 m_attachment_child_ids.insert(child_id);
625 void LuaEntitySAO::removeAttachmentChild(int child_id)
627 m_attachment_child_ids.erase(child_id);
630 UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
632 return m_attachment_child_ids;
635 ObjectProperties* LuaEntitySAO::accessObjectProperties()
640 void LuaEntitySAO::notifyObjectPropertiesModified()
642 m_properties_sent = false;
645 void LuaEntitySAO::setVelocity(v3f velocity)
647 m_velocity = velocity;
650 v3f LuaEntitySAO::getVelocity()
655 void LuaEntitySAO::setAcceleration(v3f acceleration)
657 m_acceleration = acceleration;
660 v3f LuaEntitySAO::getAcceleration()
662 return m_acceleration;
665 void LuaEntitySAO::setTextureMod(const std::string &mod)
667 std::string str = gob_cmd_set_texture_mod(mod);
668 // create message and add to list
669 ActiveObjectMessage aom(getId(), true, str);
670 m_messages_out.push(aom);
673 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
674 bool select_horiz_by_yawpitch)
676 std::string str = gob_cmd_set_sprite(
680 select_horiz_by_yawpitch
682 // create message and add to list
683 ActiveObjectMessage aom(getId(), true, str);
684 m_messages_out.push(aom);
687 std::string LuaEntitySAO::getName()
692 std::string LuaEntitySAO::getPropertyPacket()
694 return gob_cmd_set_properties(m_prop);
697 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
699 // If the object is attached client-side, don't waste bandwidth sending its position to clients
703 m_last_sent_move_precision = m_base_position.getDistanceFrom(
704 m_last_sent_position);
705 m_last_sent_position_timer = 0;
706 m_last_sent_yaw = m_yaw;
707 m_last_sent_position = m_base_position;
708 m_last_sent_velocity = m_velocity;
709 //m_last_sent_acceleration = m_acceleration;
711 float update_interval = m_env->getSendRecommendedInterval();
713 std::string str = gob_cmd_update_position(
722 // create message and add to list
723 ActiveObjectMessage aom(getId(), false, str);
724 m_messages_out.push(aom);
727 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
730 //update collision box
731 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
732 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
734 toset->MinEdge += m_base_position;
735 toset->MaxEdge += m_base_position;
743 bool LuaEntitySAO::collideWithObjects(){
744 return m_prop.collideWithObjects;
751 // No prototype, PlayerSAO does not need to be deserialized
753 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
754 UnitSAO(env_, v3f(0,0,0)),
759 m_last_good_position(0,0,0),
760 m_time_from_last_punch(0),
761 m_nocheat_dig_pos(32767, 32767, 32767),
762 m_nocheat_dig_time(0),
764 m_position_not_sent(false),
765 m_armor_groups_sent(false),
766 m_properties_sent(true),
767 m_is_singleplayer(is_singleplayer),
768 m_animation_speed(0),
769 m_animation_blend(0),
770 m_animation_loop(true),
771 m_animation_sent(false),
772 m_bone_position_sent(false),
773 m_attachment_parent_id(0),
774 m_attachment_sent(false),
775 m_breath(PLAYER_MAX_BREATH),
778 m_physics_override_speed(1),
779 m_physics_override_jump(1),
780 m_physics_override_gravity(1),
781 m_physics_override_sneak(true),
782 m_physics_override_sneak_glitch(true),
783 m_physics_override_sent(false)
785 assert(m_peer_id != 0); // pre-condition
786 m_armor_groups["fleshy"] = 100;
788 m_prop.hp_max = PLAYER_MAX_HP;
789 m_prop.physical = false;
791 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
792 // start of default appearance, this should be overwritten by LUA
793 m_prop.visual = "upright_sprite";
794 m_prop.visual_size = v2f(1, 2);
795 m_prop.textures.clear();
796 m_prop.textures.push_back("player.png");
797 m_prop.textures.push_back("player_back.png");
798 m_prop.colors.clear();
799 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
800 m_prop.spritediv = v2s16(1,1);
801 // end of default appearance
802 m_prop.is_visible = true;
803 m_prop.makes_footstep_sound = true;
804 m_hp = PLAYER_MAX_HP;
807 PlayerSAO::~PlayerSAO()
809 if(m_inventory != &m_player->inventory)
813 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
818 m_inventory = &m_player->inventory;
821 std::string PlayerSAO::getDescription()
823 return std::string("player ") + m_player->getName();
826 // Called after id has been set and has been inserted in environment
827 void PlayerSAO::addedToEnvironment(u32 dtime_s)
829 ServerActiveObject::addedToEnvironment(dtime_s);
830 ServerActiveObject::setBasePosition(m_base_position);
831 m_player->setPlayerSAO(this);
832 m_player->peer_id = m_peer_id;
833 m_last_good_position = m_base_position;
836 // Called before removing from environment
837 void PlayerSAO::removingFromEnvironment()
839 ServerActiveObject::removingFromEnvironment();
840 if (m_player->getPlayerSAO() == this) {
841 unlinkPlayerSessionAndSave();
842 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
843 it != m_attached_particle_spawners.end(); ++it) {
844 m_env->deleteParticleSpawner(*it, false);
849 bool PlayerSAO::isStaticAllowed() const
854 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
856 std::ostringstream os(std::ios::binary);
858 if(protocol_version >= 15)
860 writeU8(os, 1); // version
861 os<<serializeString(m_player->getName()); // name
862 writeU8(os, 1); // is_player
863 writeS16(os, getId()); //id
864 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
865 writeF1000(os, m_yaw);
866 writeS16(os, getHP());
868 writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
869 os<<serializeLongString(getPropertyPacket()); // message 1
870 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
871 os<<serializeLongString(gob_cmd_update_animation(
872 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
873 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
874 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
875 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
877 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
878 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
879 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
880 m_physics_override_sneak_glitch)); // 5
881 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
882 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
883 ii != m_attachment_child_ids.end(); ++ii) {
884 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
885 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
891 writeU8(os, 0); // version
892 os<<serializeString(m_player->getName()); // name
893 writeU8(os, 1); // is_player
894 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
895 writeF1000(os, m_yaw);
896 writeS16(os, getHP());
897 writeU8(os, 2); // number of messages stuffed in here
898 os<<serializeLongString(getPropertyPacket()); // message 1
899 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
906 std::string PlayerSAO::getStaticData()
908 FATAL_ERROR("Deprecated function (?)");
912 bool PlayerSAO::isAttached()
914 if(!m_attachment_parent_id)
916 // Check if the parent still exists
917 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
923 void PlayerSAO::step(float dtime, bool send_recommended)
925 if(!m_properties_sent)
927 m_properties_sent = true;
928 std::string str = getPropertyPacket();
929 // create message and add to list
930 ActiveObjectMessage aom(getId(), true, str);
931 m_messages_out.push(aom);
934 // If attached, check that our parent is still there. If it isn't, detach.
935 if(m_attachment_parent_id && !isAttached())
937 m_attachment_parent_id = 0;
938 m_attachment_bone = "";
939 m_attachment_position = v3f(0,0,0);
940 m_attachment_rotation = v3f(0,0,0);
941 setBasePosition(m_last_good_position);
942 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
945 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
947 // Set lag pool maximums based on estimated lag
948 const float LAG_POOL_MIN = 5.0;
949 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
950 if(lag_pool_max < LAG_POOL_MIN)
951 lag_pool_max = LAG_POOL_MIN;
952 m_dig_pool.setMax(lag_pool_max);
953 m_move_pool.setMax(lag_pool_max);
955 // Increment cheat prevention timers
956 m_dig_pool.add(dtime);
957 m_move_pool.add(dtime);
958 m_time_from_last_punch += dtime;
959 m_nocheat_dig_time += dtime;
961 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
962 // If the object gets detached this comes into effect automatically from the last known origin
964 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
965 m_last_good_position = pos;
966 setBasePosition(pos);
969 if (!send_recommended)
972 // If the object is attached client-side, don't waste bandwidth sending its position to clients
973 if(m_position_not_sent && !isAttached())
975 m_position_not_sent = false;
976 float update_interval = m_env->getSendRecommendedInterval();
978 if(isAttached()) // Just in case we ever do send attachment position too
979 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
981 pos = m_base_position + v3f(0,BS*1,0);
982 std::string str = gob_cmd_update_position(
991 // create message and add to list
992 ActiveObjectMessage aom(getId(), false, str);
993 m_messages_out.push(aom);
996 if (!m_armor_groups_sent) {
997 m_armor_groups_sent = true;
998 std::string str = gob_cmd_update_armor_groups(
1000 // create message and add to list
1001 ActiveObjectMessage aom(getId(), true, str);
1002 m_messages_out.push(aom);
1005 if (!m_physics_override_sent) {
1006 m_physics_override_sent = true;
1007 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1008 m_physics_override_jump, m_physics_override_gravity,
1009 m_physics_override_sneak, m_physics_override_sneak_glitch);
1010 // create message and add to list
1011 ActiveObjectMessage aom(getId(), true, str);
1012 m_messages_out.push(aom);
1015 if (!m_animation_sent) {
1016 m_animation_sent = true;
1017 std::string str = gob_cmd_update_animation(
1018 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1019 // create message and add to list
1020 ActiveObjectMessage aom(getId(), true, str);
1021 m_messages_out.push(aom);
1024 if (!m_bone_position_sent) {
1025 m_bone_position_sent = true;
1026 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1027 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1028 std::string str = gob_cmd_update_bone_position((*ii).first,
1029 (*ii).second.X, (*ii).second.Y);
1030 // create message and add to list
1031 ActiveObjectMessage aom(getId(), true, str);
1032 m_messages_out.push(aom);
1036 if (!m_attachment_sent){
1037 m_attachment_sent = true;
1038 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1039 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1040 // create message and add to list
1041 ActiveObjectMessage aom(getId(), true, str);
1042 m_messages_out.push(aom);
1046 void PlayerSAO::setBasePosition(const v3f &position)
1048 if (m_player && position != m_base_position)
1049 m_player->setDirty(true);
1051 // This needs to be ran for attachments too
1052 ServerActiveObject::setBasePosition(position);
1053 m_position_not_sent = true;
1056 void PlayerSAO::setPos(const v3f &pos)
1061 setBasePosition(pos);
1062 // Movement caused by this command is always valid
1063 m_last_good_position = pos;
1064 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1067 void PlayerSAO::moveTo(v3f pos, bool continuous)
1072 setBasePosition(pos);
1073 // Movement caused by this command is always valid
1074 m_last_good_position = pos;
1075 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1078 void PlayerSAO::setYaw(const float yaw)
1080 if (m_player && yaw != m_yaw)
1081 m_player->setDirty(true);
1083 UnitSAO::setYaw(yaw);
1086 void PlayerSAO::setYawAndSend(const float yaw)
1089 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1092 void PlayerSAO::setPitch(const float pitch)
1094 if (m_player && pitch != m_pitch)
1095 m_player->setDirty(true);
1100 void PlayerSAO::setPitchAndSend(const float pitch)
1103 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1106 int PlayerSAO::punch(v3f dir,
1107 const ToolCapabilities *toolcap,
1108 ServerActiveObject *puncher,
1109 float time_from_last_punch)
1111 // It's best that attachments cannot be punched
1118 // No effect if PvP disabled
1119 if (g_settings->getBool("enable_pvp") == false) {
1120 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1121 std::string str = gob_cmd_punched(0, getHP());
1122 // create message and add to list
1123 ActiveObjectMessage aom(getId(), true, str);
1124 m_messages_out.push(aom);
1129 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1130 time_from_last_punch);
1132 std::string punchername = "nil";
1135 punchername = puncher->getDescription();
1137 PlayerSAO *playersao = m_player->getPlayerSAO();
1139 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1140 puncher, time_from_last_punch, toolcap, dir,
1143 if (!damage_handled) {
1144 setHP(getHP() - hitparams.hp);
1145 } else { // override client prediction
1146 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1147 std::string str = gob_cmd_punched(0, getHP());
1148 // create message and add to list
1149 ActiveObjectMessage aom(getId(), true, str);
1150 m_messages_out.push(aom);
1155 actionstream << "Player " << m_player->getName() << " punched by "
1157 if (!damage_handled) {
1158 actionstream << ", damage " << hitparams.hp << " HP";
1160 actionstream << ", damage handled by lua";
1162 actionstream << std::endl;
1164 return hitparams.wear;
1167 void PlayerSAO::rightClick(ServerActiveObject *)
1171 s16 PlayerSAO::readDamage()
1173 s16 damage = m_damage;
1178 void PlayerSAO::setHP(s16 hp)
1182 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1185 hp = oldhp + hp_change;
1189 else if (hp > PLAYER_MAX_HP)
1192 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1199 m_damage += (oldhp - hp);
1201 // Update properties on death
1202 if ((hp == 0) != (oldhp == 0))
1203 m_properties_sent = false;
1206 void PlayerSAO::setBreath(const u16 breath)
1208 if (m_player && breath != m_breath)
1209 m_player->setDirty(true);
1214 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1216 m_armor_groups = armor_groups;
1217 m_armor_groups_sent = false;
1220 ItemGroupList PlayerSAO::getArmorGroups()
1222 return m_armor_groups;
1225 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1227 // store these so they can be updated to clients
1228 m_animation_range = frame_range;
1229 m_animation_speed = frame_speed;
1230 m_animation_blend = frame_blend;
1231 m_animation_loop = frame_loop;
1232 m_animation_sent = false;
1235 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1237 *frame_range = m_animation_range;
1238 *frame_speed = m_animation_speed;
1239 *frame_blend = m_animation_blend;
1240 *frame_loop = m_animation_loop;
1243 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1245 // store these so they can be updated to clients
1246 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1247 m_bone_position_sent = false;
1250 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1252 *position = m_bone_position[bone].X;
1253 *rotation = m_bone_position[bone].Y;
1256 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1258 // Attachments need to be handled on both the server and client.
1259 // If we just attach on the server, we can only copy the position of the parent. Attachments
1260 // are still sent to clients at an interval so players might see them lagging, plus we can't
1261 // read and attach to skeletal bones.
1262 // If we just attach on the client, the server still sees the child at its original location.
1263 // This breaks some things so we also give the server the most accurate representation
1264 // even if players only see the client changes.
1266 m_attachment_parent_id = parent_id;
1267 m_attachment_bone = bone;
1268 m_attachment_position = position;
1269 m_attachment_rotation = rotation;
1270 m_attachment_sent = false;
1273 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1276 *parent_id = m_attachment_parent_id;
1277 *bone = m_attachment_bone;
1278 *position = m_attachment_position;
1279 *rotation = m_attachment_rotation;
1282 void PlayerSAO::addAttachmentChild(int child_id)
1284 m_attachment_child_ids.insert(child_id);
1287 void PlayerSAO::removeAttachmentChild(int child_id)
1289 m_attachment_child_ids.erase(child_id);
1292 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1294 return m_attachment_child_ids;
1297 ObjectProperties* PlayerSAO::accessObjectProperties()
1302 void PlayerSAO::notifyObjectPropertiesModified()
1304 m_properties_sent = false;
1307 Inventory* PlayerSAO::getInventory()
1311 const Inventory* PlayerSAO::getInventory() const
1316 InventoryLocation PlayerSAO::getInventoryLocation() const
1318 InventoryLocation loc;
1319 loc.setPlayer(m_player->getName());
1323 std::string PlayerSAO::getWieldList() const
1328 int PlayerSAO::getWieldIndex() const
1330 return m_wield_index;
1333 void PlayerSAO::setWieldIndex(int i)
1335 if(i != m_wield_index) {
1340 // Erase the peer id and make the object for removal
1341 void PlayerSAO::disconnected()
1347 void PlayerSAO::unlinkPlayerSessionAndSave()
1349 assert(m_player->getPlayerSAO() == this);
1350 m_player->peer_id = 0;
1351 m_env->savePlayer(m_player);
1352 m_player->setPlayerSAO(NULL);
1353 m_env->removePlayer(m_player);
1356 std::string PlayerSAO::getPropertyPacket()
1358 m_prop.is_visible = (true);
1359 return gob_cmd_set_properties(m_prop);
1362 bool PlayerSAO::checkMovementCheat()
1364 if (isAttached() || m_is_singleplayer ||
1365 g_settings->getBool("disable_anticheat")) {
1366 m_last_good_position = m_base_position;
1370 bool cheated = false;
1372 Check player movements
1374 NOTE: Actually the server should handle player physics like the
1375 client does and compare player's position to what is calculated
1376 on our side. This is required when eg. players fly due to an
1377 explosion. Altough a node-based alternative might be possible
1378 too, and much more lightweight.
1381 float player_max_speed = 0;
1383 if (m_privs.count("fast") != 0) {
1385 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1388 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1390 // Tolerance. The lag pool does this a bit.
1391 //player_max_speed *= 2.5;
1393 v3f diff = (m_base_position - m_last_good_position);
1394 float d_vert = diff.Y;
1396 float d_horiz = diff.getLength();
1397 float required_time = d_horiz / player_max_speed;
1399 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1400 required_time = d_vert / player_max_speed; // Moving upwards
1402 if (m_move_pool.grab(required_time)) {
1403 m_last_good_position = m_base_position;
1405 actionstream << "Player " << m_player->getName()
1406 << " moved too fast; resetting position"
1408 setBasePosition(m_last_good_position);
1414 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1416 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1417 toset->MinEdge += m_base_position;
1418 toset->MaxEdge += m_base_position;
1422 bool PlayerSAO::collideWithObjects()