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(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 m_player->peer_id = 0;
842 m_env->savePlayer(m_player);
843 m_player->setPlayerSAO(NULL);
844 m_env->removePlayer(m_player);
845 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
846 it != m_attached_particle_spawners.end(); ++it) {
847 m_env->deleteParticleSpawner(*it, false);
852 bool PlayerSAO::isStaticAllowed() const
857 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
859 std::ostringstream os(std::ios::binary);
861 if(protocol_version >= 15)
863 writeU8(os, 1); // version
864 os<<serializeString(m_player->getName()); // name
865 writeU8(os, 1); // is_player
866 writeS16(os, getId()); //id
867 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
868 writeF1000(os, m_yaw);
869 writeS16(os, getHP());
871 writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
872 os<<serializeLongString(getPropertyPacket()); // message 1
873 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
874 os<<serializeLongString(gob_cmd_update_animation(
875 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
876 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
877 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
878 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
880 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
881 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
882 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
883 m_physics_override_sneak_glitch)); // 5
884 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
885 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
886 ii != m_attachment_child_ids.end(); ++ii) {
887 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
888 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
894 writeU8(os, 0); // version
895 os<<serializeString(m_player->getName()); // name
896 writeU8(os, 1); // is_player
897 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
898 writeF1000(os, m_yaw);
899 writeS16(os, getHP());
900 writeU8(os, 2); // number of messages stuffed in here
901 os<<serializeLongString(getPropertyPacket()); // message 1
902 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
909 std::string PlayerSAO::getStaticData()
911 FATAL_ERROR("Deprecated function (?)");
915 bool PlayerSAO::isAttached()
917 if(!m_attachment_parent_id)
919 // Check if the parent still exists
920 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
926 void PlayerSAO::step(float dtime, bool send_recommended)
928 if(!m_properties_sent)
930 m_properties_sent = true;
931 std::string str = getPropertyPacket();
932 // create message and add to list
933 ActiveObjectMessage aom(getId(), true, str);
934 m_messages_out.push(aom);
937 // If attached, check that our parent is still there. If it isn't, detach.
938 if(m_attachment_parent_id && !isAttached())
940 m_attachment_parent_id = 0;
941 m_attachment_bone = "";
942 m_attachment_position = v3f(0,0,0);
943 m_attachment_rotation = v3f(0,0,0);
944 setBasePosition(m_last_good_position);
945 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
948 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
950 // Set lag pool maximums based on estimated lag
951 const float LAG_POOL_MIN = 5.0;
952 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
953 if(lag_pool_max < LAG_POOL_MIN)
954 lag_pool_max = LAG_POOL_MIN;
955 m_dig_pool.setMax(lag_pool_max);
956 m_move_pool.setMax(lag_pool_max);
958 // Increment cheat prevention timers
959 m_dig_pool.add(dtime);
960 m_move_pool.add(dtime);
961 m_time_from_last_punch += dtime;
962 m_nocheat_dig_time += dtime;
964 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
965 // If the object gets detached this comes into effect automatically from the last known origin
967 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
968 m_last_good_position = pos;
969 setBasePosition(pos);
972 if (!send_recommended)
975 // If the object is attached client-side, don't waste bandwidth sending its position to clients
976 if(m_position_not_sent && !isAttached())
978 m_position_not_sent = false;
979 float update_interval = m_env->getSendRecommendedInterval();
981 if(isAttached()) // Just in case we ever do send attachment position too
982 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
984 pos = m_base_position + v3f(0,BS*1,0);
985 std::string str = gob_cmd_update_position(
994 // create message and add to list
995 ActiveObjectMessage aom(getId(), false, str);
996 m_messages_out.push(aom);
999 if (!m_armor_groups_sent) {
1000 m_armor_groups_sent = true;
1001 std::string str = gob_cmd_update_armor_groups(
1003 // create message and add to list
1004 ActiveObjectMessage aom(getId(), true, str);
1005 m_messages_out.push(aom);
1008 if (!m_physics_override_sent) {
1009 m_physics_override_sent = true;
1010 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1011 m_physics_override_jump, m_physics_override_gravity,
1012 m_physics_override_sneak, m_physics_override_sneak_glitch);
1013 // create message and add to list
1014 ActiveObjectMessage aom(getId(), true, str);
1015 m_messages_out.push(aom);
1018 if (!m_animation_sent) {
1019 m_animation_sent = true;
1020 std::string str = gob_cmd_update_animation(
1021 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1022 // create message and add to list
1023 ActiveObjectMessage aom(getId(), true, str);
1024 m_messages_out.push(aom);
1027 if (!m_bone_position_sent) {
1028 m_bone_position_sent = true;
1029 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1030 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1031 std::string str = gob_cmd_update_bone_position((*ii).first,
1032 (*ii).second.X, (*ii).second.Y);
1033 // create message and add to list
1034 ActiveObjectMessage aom(getId(), true, str);
1035 m_messages_out.push(aom);
1039 if (!m_attachment_sent){
1040 m_attachment_sent = true;
1041 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1042 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1043 // create message and add to list
1044 ActiveObjectMessage aom(getId(), true, str);
1045 m_messages_out.push(aom);
1049 void PlayerSAO::setBasePosition(const v3f &position)
1051 if (m_player && position != m_base_position)
1052 m_player->setDirty(true);
1054 // This needs to be ran for attachments too
1055 ServerActiveObject::setBasePosition(position);
1056 m_position_not_sent = true;
1059 void PlayerSAO::setPos(const v3f &pos)
1064 setBasePosition(pos);
1065 // Movement caused by this command is always valid
1066 m_last_good_position = pos;
1067 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1070 void PlayerSAO::moveTo(v3f pos, bool continuous)
1075 setBasePosition(pos);
1076 // Movement caused by this command is always valid
1077 m_last_good_position = pos;
1078 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1081 void PlayerSAO::setYaw(const float yaw, bool send_data)
1083 if (m_player && yaw != m_yaw)
1084 m_player->setDirty(true);
1086 UnitSAO::setYaw(yaw);
1088 // Datas should not be sent at player initialization
1090 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1093 void PlayerSAO::setPitch(const float pitch, bool send_data)
1095 if (m_player && pitch != m_pitch)
1096 m_player->setDirty(true);
1101 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1104 int PlayerSAO::punch(v3f dir,
1105 const ToolCapabilities *toolcap,
1106 ServerActiveObject *puncher,
1107 float time_from_last_punch)
1109 // It's best that attachments cannot be punched
1116 // No effect if PvP disabled
1117 if (g_settings->getBool("enable_pvp") == false) {
1118 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1119 std::string str = gob_cmd_punched(0, getHP());
1120 // create message and add to list
1121 ActiveObjectMessage aom(getId(), true, str);
1122 m_messages_out.push(aom);
1127 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1128 time_from_last_punch);
1130 std::string punchername = "nil";
1133 punchername = puncher->getDescription();
1135 PlayerSAO *playersao = m_player->getPlayerSAO();
1137 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1138 puncher, time_from_last_punch, toolcap, dir,
1141 if (!damage_handled) {
1142 setHP(getHP() - hitparams.hp);
1143 } else { // override client prediction
1144 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1145 std::string str = gob_cmd_punched(0, getHP());
1146 // create message and add to list
1147 ActiveObjectMessage aom(getId(), true, str);
1148 m_messages_out.push(aom);
1153 actionstream << "Player " << m_player->getName() << " punched by "
1155 if (!damage_handled) {
1156 actionstream << ", damage " << hitparams.hp << " HP";
1158 actionstream << ", damage handled by lua";
1160 actionstream << std::endl;
1162 return hitparams.wear;
1165 void PlayerSAO::rightClick(ServerActiveObject *)
1169 s16 PlayerSAO::readDamage()
1171 s16 damage = m_damage;
1176 void PlayerSAO::setHP(s16 hp, bool direct)
1185 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1188 hp = oldhp + hp_change;
1192 else if (hp > PLAYER_MAX_HP)
1195 if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1202 m_damage += (oldhp - hp);
1204 // Update properties on death
1205 if ((hp == 0) != (oldhp == 0))
1206 m_properties_sent = false;
1209 void PlayerSAO::setBreath(const u16 breath)
1211 if (m_player && breath != m_breath)
1212 m_player->setDirty(true);
1217 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1219 m_armor_groups = armor_groups;
1220 m_armor_groups_sent = false;
1223 ItemGroupList PlayerSAO::getArmorGroups()
1225 return m_armor_groups;
1228 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1230 // store these so they can be updated to clients
1231 m_animation_range = frame_range;
1232 m_animation_speed = frame_speed;
1233 m_animation_blend = frame_blend;
1234 m_animation_loop = frame_loop;
1235 m_animation_sent = false;
1238 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1240 *frame_range = m_animation_range;
1241 *frame_speed = m_animation_speed;
1242 *frame_blend = m_animation_blend;
1243 *frame_loop = m_animation_loop;
1246 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1248 // store these so they can be updated to clients
1249 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1250 m_bone_position_sent = false;
1253 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1255 *position = m_bone_position[bone].X;
1256 *rotation = m_bone_position[bone].Y;
1259 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1261 // Attachments need to be handled on both the server and client.
1262 // If we just attach on the server, we can only copy the position of the parent. Attachments
1263 // are still sent to clients at an interval so players might see them lagging, plus we can't
1264 // read and attach to skeletal bones.
1265 // If we just attach on the client, the server still sees the child at its original location.
1266 // This breaks some things so we also give the server the most accurate representation
1267 // even if players only see the client changes.
1269 m_attachment_parent_id = parent_id;
1270 m_attachment_bone = bone;
1271 m_attachment_position = position;
1272 m_attachment_rotation = rotation;
1273 m_attachment_sent = false;
1276 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1279 *parent_id = m_attachment_parent_id;
1280 *bone = m_attachment_bone;
1281 *position = m_attachment_position;
1282 *rotation = m_attachment_rotation;
1285 void PlayerSAO::addAttachmentChild(int child_id)
1287 m_attachment_child_ids.insert(child_id);
1290 void PlayerSAO::removeAttachmentChild(int child_id)
1292 m_attachment_child_ids.erase(child_id);
1295 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1297 return m_attachment_child_ids;
1300 ObjectProperties* PlayerSAO::accessObjectProperties()
1305 void PlayerSAO::notifyObjectPropertiesModified()
1307 m_properties_sent = false;
1310 Inventory* PlayerSAO::getInventory()
1314 const Inventory* PlayerSAO::getInventory() const
1319 InventoryLocation PlayerSAO::getInventoryLocation() const
1321 InventoryLocation loc;
1322 loc.setPlayer(m_player->getName());
1326 std::string PlayerSAO::getWieldList() const
1331 int PlayerSAO::getWieldIndex() const
1333 return m_wield_index;
1336 void PlayerSAO::setWieldIndex(int i)
1338 if(i != m_wield_index) {
1343 void PlayerSAO::disconnected()
1347 if(m_player->getPlayerSAO() == this)
1349 m_player->setPlayerSAO(NULL);
1350 m_player->peer_id = 0;
1354 std::string PlayerSAO::getPropertyPacket()
1356 m_prop.is_visible = (true);
1357 return gob_cmd_set_properties(m_prop);
1360 bool PlayerSAO::checkMovementCheat()
1362 if (isAttached() || m_is_singleplayer ||
1363 g_settings->getBool("disable_anticheat")) {
1364 m_last_good_position = m_base_position;
1368 bool cheated = false;
1370 Check player movements
1372 NOTE: Actually the server should handle player physics like the
1373 client does and compare player's position to what is calculated
1374 on our side. This is required when eg. players fly due to an
1375 explosion. Altough a node-based alternative might be possible
1376 too, and much more lightweight.
1379 float player_max_speed = 0;
1381 if (m_privs.count("fast") != 0) {
1383 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1386 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1388 // Tolerance. The lag pool does this a bit.
1389 //player_max_speed *= 2.5;
1391 v3f diff = (m_base_position - m_last_good_position);
1392 float d_vert = diff.Y;
1394 float d_horiz = diff.getLength();
1395 float required_time = d_horiz / player_max_speed;
1397 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1398 required_time = d_vert / player_max_speed; // Moving upwards
1400 if (m_move_pool.grab(required_time)) {
1401 m_last_good_position = m_base_position;
1403 actionstream << "Player " << m_player->getName()
1404 << " moved too fast; resetting position"
1406 setBasePosition(m_last_good_position);
1412 bool PlayerSAO::getCollisionBox(aabb3f *toset)
1414 *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1415 toset->MinEdge += m_base_position;
1416 toset->MaxEdge += m_base_position;
1420 bool PlayerSAO::collideWithObjects()