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):
121 ServerActiveObject(env, pos),
127 m_acceleration(0,0,0),
129 m_properties_sent(true),
131 m_last_sent_position(0,0,0),
132 m_last_sent_velocity(0,0,0),
133 m_last_sent_position_timer(0),
134 m_last_sent_move_precision(0),
135 m_armor_groups_sent(false),
136 m_animation_speed(0),
137 m_animation_blend(0),
138 m_animation_loop(true),
139 m_animation_sent(false),
140 m_bone_position_sent(false),
141 m_attachment_parent_id(0),
142 m_attachment_sent(false)
144 // Only register type if no environment supplied
146 ServerActiveObject::registerType(getType(), create);
150 // Initialize something to armor groups
151 m_armor_groups["fleshy"] = 100;
154 LuaEntitySAO::~LuaEntitySAO()
157 m_env->getScriptIface()->luaentity_Remove(m_id);
161 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
163 ServerActiveObject::addedToEnvironment(dtime_s);
165 // Create entity from name
166 m_registered = m_env->getScriptIface()->
167 luaentity_Add(m_id, m_init_name.c_str());
171 m_env->getScriptIface()->
172 luaentity_GetProperties(m_id, &m_prop);
173 // Initialize HP from properties
174 m_hp = m_prop.hp_max;
175 // Activate entity, supplying serialized state
176 m_env->getScriptIface()->
177 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
179 m_prop.infotext = m_init_name;
183 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
184 const std::string &data)
192 std::istringstream is(data, std::ios::binary);
194 u8 version = readU8(is);
195 // check if version is supported
197 name = deSerializeString(is);
198 state = deSerializeLongString(is);
200 else if(version == 1){
201 name = deSerializeString(is);
202 state = deSerializeLongString(is);
204 velocity = readV3F1000(is);
209 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
210 <<state<<"\")"<<std::endl;
211 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
213 sao->m_velocity = velocity;
218 bool LuaEntitySAO::isAttached()
220 if(!m_attachment_parent_id)
222 // Check if the parent still exists
223 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
229 void LuaEntitySAO::step(float dtime, bool send_recommended)
231 if(!m_properties_sent)
233 m_properties_sent = true;
234 std::string str = getPropertyPacket();
235 // create message and add to list
236 ActiveObjectMessage aom(getId(), true, str);
237 m_messages_out.push(aom);
240 // If attached, check that our parent is still there. If it isn't, detach.
241 if(m_attachment_parent_id && !isAttached())
243 m_attachment_parent_id = 0;
244 m_attachment_bone = "";
245 m_attachment_position = v3f(0,0,0);
246 m_attachment_rotation = v3f(0,0,0);
247 sendPosition(false, true);
250 m_last_sent_position_timer += dtime;
252 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
253 // If the object gets detached this comes into effect automatically from the last known origin
256 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
257 m_base_position = pos;
258 m_velocity = v3f(0,0,0);
259 m_acceleration = v3f(0,0,0);
264 aabb3f box = m_prop.collisionbox;
267 collisionMoveResult moveresult;
268 f32 pos_max_d = BS*0.25; // Distance per iteration
269 v3f p_pos = m_base_position;
270 v3f p_velocity = m_velocity;
271 v3f p_acceleration = m_acceleration;
272 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
273 pos_max_d, box, m_prop.stepheight, dtime,
274 &p_pos, &p_velocity, p_acceleration,
275 this, m_prop.collideWithObjects);
278 m_base_position = p_pos;
279 m_velocity = p_velocity;
280 m_acceleration = p_acceleration;
282 m_base_position += dtime * m_velocity + 0.5 * dtime
283 * dtime * m_acceleration;
284 m_velocity += dtime * m_acceleration;
287 if((m_prop.automatic_face_movement_dir) &&
288 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
290 float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
291 + m_prop.automatic_face_movement_dir_offset;
292 float max_rotation_delta =
293 dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
295 if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
296 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
298 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
306 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
309 if(send_recommended == false)
314 // TODO: force send when acceleration changes enough?
315 float minchange = 0.2*BS;
316 if(m_last_sent_position_timer > 1.0){
318 } else if(m_last_sent_position_timer > 0.2){
321 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
322 move_d += m_last_sent_move_precision;
323 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
324 if(move_d > minchange || vel_d > minchange ||
325 fabs(m_yaw - m_last_sent_yaw) > 1.0){
326 sendPosition(true, false);
330 if(m_armor_groups_sent == false){
331 m_armor_groups_sent = true;
332 std::string str = gob_cmd_update_armor_groups(
334 // create message and add to list
335 ActiveObjectMessage aom(getId(), true, str);
336 m_messages_out.push(aom);
339 if(m_animation_sent == false){
340 m_animation_sent = true;
341 std::string str = gob_cmd_update_animation(
342 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
343 // create message and add to list
344 ActiveObjectMessage aom(getId(), true, str);
345 m_messages_out.push(aom);
348 if(m_bone_position_sent == false){
349 m_bone_position_sent = true;
350 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
351 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
352 std::string str = gob_cmd_update_bone_position((*ii).first,
353 (*ii).second.X, (*ii).second.Y);
354 // create message and add to list
355 ActiveObjectMessage aom(getId(), true, str);
356 m_messages_out.push(aom);
360 if(m_attachment_sent == false){
361 m_attachment_sent = true;
362 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
363 // create message and add to list
364 ActiveObjectMessage aom(getId(), true, str);
365 m_messages_out.push(aom);
369 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
371 std::ostringstream os(std::ios::binary);
373 if(protocol_version >= 14)
375 writeU8(os, 1); // version
376 os<<serializeString(""); // name
377 writeU8(os, 0); // is_player
378 writeS16(os, getId()); //id
379 writeV3F1000(os, m_base_position);
380 writeF1000(os, m_yaw);
383 writeU8(os, 4 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
384 os<<serializeLongString(getPropertyPacket()); // message 1
385 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
386 os<<serializeLongString(gob_cmd_update_animation(
387 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
388 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
389 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
390 os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
391 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
393 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
394 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
395 (ii != m_attachment_child_ids.end()); ++ii) {
396 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
397 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
403 writeU8(os, 0); // version
404 os<<serializeString(""); // name
405 writeU8(os, 0); // is_player
406 writeV3F1000(os, m_base_position);
407 writeF1000(os, m_yaw);
409 writeU8(os, 2); // number of messages stuffed in here
410 os<<serializeLongString(getPropertyPacket()); // message 1
411 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
418 std::string LuaEntitySAO::getStaticData()
420 verbosestream<<FUNCTION_NAME<<std::endl;
421 std::ostringstream os(std::ios::binary);
425 os<<serializeString(m_init_name);
428 std::string state = m_env->getScriptIface()->
429 luaentity_GetStaticdata(m_id);
430 os<<serializeLongString(state);
432 os<<serializeLongString(m_init_state);
437 writeV3F1000(os, m_velocity);
439 writeF1000(os, m_yaw);
443 int LuaEntitySAO::punch(v3f dir,
444 const ToolCapabilities *toolcap,
445 ServerActiveObject *puncher,
446 float time_from_last_punch)
449 // Delete unknown LuaEntities when punched
454 // It's best that attachments cannot be punched
458 ItemStack *punchitem = NULL;
459 ItemStack punchitem_static;
461 punchitem_static = puncher->getWieldedItem();
462 punchitem = &punchitem_static;
465 PunchDamageResult result = getPunchDamage(
469 time_from_last_punch);
471 if (result.did_punch) {
472 setHP(getHP() - result.damage);
474 if (result.damage > 0) {
475 std::string punchername = puncher ? puncher->getDescription() : "nil";
477 actionstream << getDescription() << " punched by "
478 << punchername << ", damage " << result.damage
479 << " hp, health now " << getHP() << " hp" << std::endl;
482 std::string str = gob_cmd_punched(result.damage, getHP());
483 // create message and add to list
484 ActiveObjectMessage aom(getId(), true, str);
485 m_messages_out.push(aom);
491 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
492 time_from_last_punch, toolcap, dir);
497 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
501 // It's best that attachments cannot be clicked
504 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
507 void LuaEntitySAO::setPos(v3f pos)
511 m_base_position = pos;
512 sendPosition(false, true);
515 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
519 m_base_position = pos;
521 sendPosition(true, true);
524 float LuaEntitySAO::getMinimumSavedMovement()
529 std::string LuaEntitySAO::getDescription()
531 std::ostringstream os(std::ios::binary);
532 os<<"LuaEntitySAO at (";
533 os<<(m_base_position.X/BS)<<",";
534 os<<(m_base_position.Y/BS)<<",";
535 os<<(m_base_position.Z/BS);
540 void LuaEntitySAO::setHP(s16 hp)
546 s16 LuaEntitySAO::getHP() const
551 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
553 m_armor_groups = armor_groups;
554 m_armor_groups_sent = false;
557 ItemGroupList LuaEntitySAO::getArmorGroups()
559 return m_armor_groups;
562 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
564 m_animation_range = frame_range;
565 m_animation_speed = frame_speed;
566 m_animation_blend = frame_blend;
567 m_animation_loop = frame_loop;
568 m_animation_sent = false;
571 void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
573 *frame_range = m_animation_range;
574 *frame_speed = m_animation_speed;
575 *frame_blend = m_animation_blend;
576 *frame_loop = m_animation_loop;
579 void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
581 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
582 m_bone_position_sent = false;
585 void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
587 *position = m_bone_position[bone].X;
588 *rotation = m_bone_position[bone].Y;
591 void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
593 // Attachments need to be handled on both the server and client.
594 // If we just attach on the server, we can only copy the position of the parent. Attachments
595 // are still sent to clients at an interval so players might see them lagging, plus we can't
596 // read and attach to skeletal bones.
597 // If we just attach on the client, the server still sees the child at its original location.
598 // This breaks some things so we also give the server the most accurate representation
599 // even if players only see the client changes.
601 m_attachment_parent_id = parent_id;
602 m_attachment_bone = bone;
603 m_attachment_position = position;
604 m_attachment_rotation = rotation;
605 m_attachment_sent = false;
608 void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
611 *parent_id = m_attachment_parent_id;
612 *bone = m_attachment_bone;
613 *position = m_attachment_position;
614 *rotation = m_attachment_rotation;
617 void LuaEntitySAO::addAttachmentChild(int child_id)
619 m_attachment_child_ids.insert(child_id);
622 void LuaEntitySAO::removeAttachmentChild(int child_id)
624 m_attachment_child_ids.erase(child_id);
627 UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
629 return m_attachment_child_ids;
632 ObjectProperties* LuaEntitySAO::accessObjectProperties()
637 void LuaEntitySAO::notifyObjectPropertiesModified()
639 m_properties_sent = false;
642 void LuaEntitySAO::setVelocity(v3f velocity)
644 m_velocity = velocity;
647 v3f LuaEntitySAO::getVelocity()
652 void LuaEntitySAO::setAcceleration(v3f acceleration)
654 m_acceleration = acceleration;
657 v3f LuaEntitySAO::getAcceleration()
659 return m_acceleration;
662 void LuaEntitySAO::setYaw(float yaw)
667 float LuaEntitySAO::getYaw()
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_, RemotePlayer *player_, u16 peer_id_,
761 const std::set<std::string> &privs, bool is_singleplayer):
762 ServerActiveObject(env_, v3f(0,0,0)),
767 m_last_good_position(0,0,0),
768 m_time_from_last_punch(0),
769 m_nocheat_dig_pos(32767, 32767, 32767),
770 m_nocheat_dig_time(0),
772 m_position_not_sent(false),
773 m_armor_groups_sent(false),
774 m_properties_sent(true),
776 m_is_singleplayer(is_singleplayer),
777 m_animation_speed(0),
778 m_animation_blend(0),
779 m_animation_loop(true),
780 m_animation_sent(false),
781 m_bone_position_sent(false),
782 m_attachment_parent_id(0),
783 m_attachment_sent(false),
785 m_physics_override_speed(1),
786 m_physics_override_jump(1),
787 m_physics_override_gravity(1),
788 m_physics_override_sneak(true),
789 m_physics_override_sneak_glitch(true),
790 m_physics_override_sent(false)
792 assert(m_player); // pre-condition
793 assert(m_peer_id != 0); // pre-condition
794 setBasePosition(m_player->getPosition());
795 m_inventory = &m_player->inventory;
796 m_armor_groups["fleshy"] = 100;
798 m_prop.hp_max = PLAYER_MAX_HP;
799 m_prop.physical = false;
801 m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
802 // start of default appearance, this should be overwritten by LUA
803 m_prop.visual = "upright_sprite";
804 m_prop.visual_size = v2f(1, 2);
805 m_prop.textures.clear();
806 m_prop.textures.push_back("player.png");
807 m_prop.textures.push_back("player_back.png");
808 m_prop.colors.clear();
809 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
810 m_prop.spritediv = v2s16(1,1);
811 // end of default appearance
812 m_prop.is_visible = true;
813 m_prop.makes_footstep_sound = true;
816 PlayerSAO::~PlayerSAO()
818 if(m_inventory != &m_player->inventory)
823 std::string PlayerSAO::getDescription()
825 return std::string("player ") + m_player->getName();
828 // Called after id has been set and has been inserted in environment
829 void PlayerSAO::addedToEnvironment(u32 dtime_s)
831 ServerActiveObject::addedToEnvironment(dtime_s);
832 ServerActiveObject::setBasePosition(m_player->getPosition());
833 m_player->setPlayerSAO(this);
834 m_player->peer_id = m_peer_id;
835 m_last_good_position = m_player->getPosition();
838 // Called before removing from environment
839 void PlayerSAO::removingFromEnvironment()
841 ServerActiveObject::removingFromEnvironment();
842 if (m_player->getPlayerSAO() == this) {
843 m_player->setPlayerSAO(NULL);
844 m_player->peer_id = 0;
845 m_env->savePlayer(m_player);
846 m_env->removePlayer(m_player);
850 bool PlayerSAO::isStaticAllowed() const
855 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
857 std::ostringstream os(std::ios::binary);
859 if(protocol_version >= 15)
861 writeU8(os, 1); // version
862 os<<serializeString(m_player->getName()); // name
863 writeU8(os, 1); // is_player
864 writeS16(os, getId()); //id
865 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
866 writeF1000(os, m_player->getYaw());
867 writeS16(os, getHP());
869 writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
870 os<<serializeLongString(getPropertyPacket()); // message 1
871 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
872 os<<serializeLongString(gob_cmd_update_animation(
873 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
874 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
875 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
876 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
878 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
879 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
880 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
881 m_physics_override_sneak_glitch)); // 5
882 os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
883 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
884 ii != m_attachment_child_ids.end(); ++ii) {
885 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
886 os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
892 writeU8(os, 0); // version
893 os<<serializeString(m_player->getName()); // name
894 writeU8(os, 1); // is_player
895 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
896 writeF1000(os, m_player->getYaw());
897 writeS16(os, getHP());
898 writeU8(os, 2); // number of messages stuffed in here
899 os<<serializeLongString(getPropertyPacket()); // message 1
900 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
907 std::string PlayerSAO::getStaticData()
909 FATAL_ERROR("Deprecated function (?)");
913 bool PlayerSAO::isAttached()
915 if(!m_attachment_parent_id)
917 // Check if the parent still exists
918 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
924 void PlayerSAO::step(float dtime, bool send_recommended)
926 if(!m_properties_sent)
928 m_properties_sent = true;
929 std::string str = getPropertyPacket();
930 // create message and add to list
931 ActiveObjectMessage aom(getId(), true, str);
932 m_messages_out.push(aom);
935 // If attached, check that our parent is still there. If it isn't, detach.
936 if(m_attachment_parent_id && !isAttached())
938 m_attachment_parent_id = 0;
939 m_attachment_bone = "";
940 m_attachment_position = v3f(0,0,0);
941 m_attachment_rotation = v3f(0,0,0);
942 m_player->setPosition(m_last_good_position);
943 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
946 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
948 // Set lag pool maximums based on estimated lag
949 const float LAG_POOL_MIN = 5.0;
950 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
951 if(lag_pool_max < LAG_POOL_MIN)
952 lag_pool_max = LAG_POOL_MIN;
953 m_dig_pool.setMax(lag_pool_max);
954 m_move_pool.setMax(lag_pool_max);
956 // Increment cheat prevention timers
957 m_dig_pool.add(dtime);
958 m_move_pool.add(dtime);
959 m_time_from_last_punch += dtime;
960 m_nocheat_dig_time += dtime;
962 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
963 // If the object gets detached this comes into effect automatically from the last known origin
966 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
967 m_last_good_position = pos;
968 m_player->setPosition(pos);
971 if(send_recommended == false)
974 // If the object is attached client-side, don't waste bandwidth sending its position to clients
975 if(m_position_not_sent && !isAttached())
977 m_position_not_sent = false;
978 float update_interval = m_env->getSendRecommendedInterval();
980 if(isAttached()) // Just in case we ever do send attachment position too
981 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
983 pos = m_player->getPosition() + v3f(0,BS*1,0);
984 std::string str = gob_cmd_update_position(
993 // create message and add to list
994 ActiveObjectMessage aom(getId(), false, str);
995 m_messages_out.push(aom);
998 if(m_armor_groups_sent == false) {
999 m_armor_groups_sent = true;
1000 std::string str = gob_cmd_update_armor_groups(
1002 // create message and add to list
1003 ActiveObjectMessage aom(getId(), true, str);
1004 m_messages_out.push(aom);
1007 if(m_physics_override_sent == false){
1008 m_physics_override_sent = true;
1009 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1010 m_physics_override_jump, m_physics_override_gravity,
1011 m_physics_override_sneak, m_physics_override_sneak_glitch);
1012 // create message and add to list
1013 ActiveObjectMessage aom(getId(), true, str);
1014 m_messages_out.push(aom);
1017 if(m_animation_sent == false){
1018 m_animation_sent = true;
1019 std::string str = gob_cmd_update_animation(
1020 m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1021 // create message and add to list
1022 ActiveObjectMessage aom(getId(), true, str);
1023 m_messages_out.push(aom);
1026 if (!m_bone_position_sent) {
1027 m_bone_position_sent = true;
1028 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1029 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1030 std::string str = gob_cmd_update_bone_position((*ii).first,
1031 (*ii).second.X, (*ii).second.Y);
1032 // create message and add to list
1033 ActiveObjectMessage aom(getId(), true, str);
1034 m_messages_out.push(aom);
1038 if (!m_attachment_sent){
1039 m_attachment_sent = true;
1040 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1041 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1042 // create message and add to list
1043 ActiveObjectMessage aom(getId(), true, str);
1044 m_messages_out.push(aom);
1048 void PlayerSAO::setBasePosition(const v3f &position)
1050 // This needs to be ran for attachments too
1051 ServerActiveObject::setBasePosition(position);
1052 m_position_not_sent = true;
1055 void PlayerSAO::setPos(v3f pos)
1059 m_player->setPosition(pos);
1060 // Movement caused by this command is always valid
1061 m_last_good_position = pos;
1062 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1065 void PlayerSAO::moveTo(v3f pos, bool continuous)
1069 m_player->setPosition(pos);
1070 // Movement caused by this command is always valid
1071 m_last_good_position = pos;
1072 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1075 void PlayerSAO::setYaw(float yaw)
1077 m_player->setYaw(yaw);
1078 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1081 void PlayerSAO::setPitch(float pitch)
1083 m_player->setPitch(pitch);
1084 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1087 int PlayerSAO::punch(v3f dir,
1088 const ToolCapabilities *toolcap,
1089 ServerActiveObject *puncher,
1090 float time_from_last_punch)
1092 // It's best that attachments cannot be punched
1099 // No effect if PvP disabled
1100 if (g_settings->getBool("enable_pvp") == false) {
1101 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1102 std::string str = gob_cmd_punched(0, getHP());
1103 // create message and add to list
1104 ActiveObjectMessage aom(getId(), true, str);
1105 m_messages_out.push(aom);
1110 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1111 time_from_last_punch);
1113 std::string punchername = "nil";
1116 punchername = puncher->getDescription();
1118 PlayerSAO *playersao = m_player->getPlayerSAO();
1120 bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1121 puncher, time_from_last_punch, toolcap, dir,
1124 if (!damage_handled) {
1125 setHP(getHP() - hitparams.hp);
1126 } else { // override client prediction
1127 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1128 std::string str = gob_cmd_punched(0, getHP());
1129 // create message and add to list
1130 ActiveObjectMessage aom(getId(), true, str);
1131 m_messages_out.push(aom);
1136 actionstream << "Player " << m_player->getName() << " punched by "
1138 if (!damage_handled) {
1139 actionstream << ", damage " << hitparams.hp << " HP";
1141 actionstream << ", damage handled by lua";
1143 actionstream << std::endl;
1145 return hitparams.wear;
1148 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1152 s16 PlayerSAO::getHP() const
1154 return m_player->hp;
1157 s16 PlayerSAO::readDamage()
1159 s16 damage = m_damage;
1164 void PlayerSAO::setHP(s16 hp)
1166 s16 oldhp = m_player->hp;
1168 s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1172 hp = oldhp + hp_change;
1176 else if (hp > PLAYER_MAX_HP)
1179 if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1186 m_damage += (oldhp - hp);
1188 // Update properties on death
1189 if ((hp == 0) != (oldhp == 0))
1190 m_properties_sent = false;
1193 u16 PlayerSAO::getBreath() const
1195 return m_player->getBreath();
1198 void PlayerSAO::setBreath(u16 breath)
1200 m_player->setBreath(breath);
1203 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1205 m_armor_groups = armor_groups;
1206 m_armor_groups_sent = false;
1209 ItemGroupList PlayerSAO::getArmorGroups()
1211 return m_armor_groups;
1214 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1216 // store these so they can be updated to clients
1217 m_animation_range = frame_range;
1218 m_animation_speed = frame_speed;
1219 m_animation_blend = frame_blend;
1220 m_animation_loop = frame_loop;
1221 m_animation_sent = false;
1224 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1226 *frame_range = m_animation_range;
1227 *frame_speed = m_animation_speed;
1228 *frame_blend = m_animation_blend;
1229 *frame_loop = m_animation_loop;
1232 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1234 // store these so they can be updated to clients
1235 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1236 m_bone_position_sent = false;
1239 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1241 *position = m_bone_position[bone].X;
1242 *rotation = m_bone_position[bone].Y;
1245 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1247 // Attachments need to be handled on both the server and client.
1248 // If we just attach on the server, we can only copy the position of the parent. Attachments
1249 // are still sent to clients at an interval so players might see them lagging, plus we can't
1250 // read and attach to skeletal bones.
1251 // If we just attach on the client, the server still sees the child at its original location.
1252 // This breaks some things so we also give the server the most accurate representation
1253 // even if players only see the client changes.
1255 m_attachment_parent_id = parent_id;
1256 m_attachment_bone = bone;
1257 m_attachment_position = position;
1258 m_attachment_rotation = rotation;
1259 m_attachment_sent = false;
1262 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1265 *parent_id = m_attachment_parent_id;
1266 *bone = m_attachment_bone;
1267 *position = m_attachment_position;
1268 *rotation = m_attachment_rotation;
1271 void PlayerSAO::addAttachmentChild(int child_id)
1273 m_attachment_child_ids.insert(child_id);
1276 void PlayerSAO::removeAttachmentChild(int child_id)
1278 m_attachment_child_ids.erase(child_id);
1281 UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
1283 return m_attachment_child_ids;
1286 ObjectProperties* PlayerSAO::accessObjectProperties()
1291 void PlayerSAO::notifyObjectPropertiesModified()
1293 m_properties_sent = false;
1296 Inventory* PlayerSAO::getInventory()
1300 const Inventory* PlayerSAO::getInventory() const
1305 InventoryLocation PlayerSAO::getInventoryLocation() const
1307 InventoryLocation loc;
1308 loc.setPlayer(m_player->getName());
1312 std::string PlayerSAO::getWieldList() const
1317 int PlayerSAO::getWieldIndex() const
1319 return m_wield_index;
1322 void PlayerSAO::setWieldIndex(int i)
1324 if(i != m_wield_index) {
1329 void PlayerSAO::disconnected()
1333 if(m_player->getPlayerSAO() == this)
1335 m_player->setPlayerSAO(NULL);
1336 m_player->peer_id = 0;
1340 std::string PlayerSAO::getPropertyPacket()
1342 m_prop.is_visible = (true);
1343 return gob_cmd_set_properties(m_prop);
1346 bool PlayerSAO::checkMovementCheat()
1348 if (isAttached() || m_is_singleplayer ||
1349 g_settings->getBool("disable_anticheat")) {
1350 m_last_good_position = m_player->getPosition();
1354 bool cheated = false;
1356 Check player movements
1358 NOTE: Actually the server should handle player physics like the
1359 client does and compare player's position to what is calculated
1360 on our side. This is required when eg. players fly due to an
1361 explosion. Altough a node-based alternative might be possible
1362 too, and much more lightweight.
1365 float player_max_speed = 0;
1367 if (m_privs.count("fast") != 0) {
1369 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1372 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1374 // Tolerance. The lag pool does this a bit.
1375 //player_max_speed *= 2.5;
1377 v3f diff = (m_player->getPosition() - m_last_good_position);
1378 float d_vert = diff.Y;
1380 float d_horiz = diff.getLength();
1381 float required_time = d_horiz / player_max_speed;
1383 if (d_vert > 0 && d_vert / player_max_speed > required_time)
1384 required_time = d_vert / player_max_speed; // Moving upwards
1386 if (m_move_pool.grab(required_time)) {
1387 m_last_good_position = m_player->getPosition();
1389 actionstream << "Player " << m_player->getName()
1390 << " moved too fast; resetting position"
1392 m_player->setPosition(m_last_good_position);
1398 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1399 //update collision box
1400 *toset = m_player->getCollisionbox();
1402 toset->MinEdge += m_base_position;
1403 toset->MaxEdge += m_base_position;
1408 bool PlayerSAO::collideWithObjects(){