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 "collision.h"
22 #include "environment.h"
24 #include "main.h" // For g_profiler
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "cpp_api/scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
34 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class DummyLoadSAO : public ServerActiveObject
43 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44 ServerActiveObject(env, pos)
46 ServerActiveObject::registerType(type, create);
48 // Pretend to be the test object (to fool the client)
50 { return ACTIVEOBJECT_TYPE_TEST; }
51 // And never save to disk
52 bool isStaticAllowed() const
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new DummyLoadSAO(env, pos, 0);
61 void step(float dtime, bool send_recommended)
64 infostream<<"DummyLoadSAO step"<<std::endl;
67 bool getCollisionBox(aabb3f *toset) {
74 // Prototype (registers item for deserialization)
75 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
76 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
77 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
78 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
84 class TestSAO : public ServerActiveObject
87 TestSAO(ServerEnvironment *env, v3f pos):
88 ServerActiveObject(env, pos),
92 ServerActiveObject::registerType(getType(), create);
95 { return ACTIVEOBJECT_TYPE_TEST; }
97 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
98 const std::string &data)
100 return new TestSAO(env, pos);
103 void step(float dtime, bool send_recommended)
112 m_base_position.Y += dtime * BS * 2;
113 if(m_base_position.Y > 8*BS)
114 m_base_position.Y = 2*BS;
116 if(send_recommended == false)
126 data += itos(0); // 0 = position
128 data += itos(m_base_position.X);
130 data += itos(m_base_position.Y);
132 data += itos(m_base_position.Z);
134 ActiveObjectMessage aom(getId(), false, data);
135 m_messages_out.push_back(aom);
139 bool getCollisionBox(aabb3f *toset) {
148 // Prototype (registers item for deserialization)
149 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
154 DEPRECATED: New dropped items are implemented in Lua; see
155 builtin/item_entity.lua.
158 class ItemSAO : public ServerActiveObject
162 { return ACTIVEOBJECT_TYPE_ITEM; }
164 float getMinimumSavedMovement()
167 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
168 const std::string &data)
170 std::istringstream is(data, std::ios::binary);
175 // check if version is supported
178 std::string itemstring = deSerializeString(is);
179 infostream<<"create(): Creating item \""
180 <<itemstring<<"\""<<std::endl;
181 return new ItemSAO(env, pos, itemstring);
184 ItemSAO(ServerEnvironment *env, v3f pos,
185 const std::string itemstring):
186 ServerActiveObject(env, pos),
187 m_itemstring(itemstring),
188 m_itemstring_changed(false),
190 m_last_sent_position(0,0,0)
192 ServerActiveObject::registerType(getType(), create);
195 void step(float dtime, bool send_recommended)
197 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
201 const float interval = 0.2;
202 if(m_move_interval.step(dtime, interval)==false)
206 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
207 collisionMoveResult moveresult;
209 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
210 // Maximum movement without glitches
211 f32 pos_max_d = BS*0.25;
213 if(m_speed_f.getLength()*dtime > pos_max_d)
214 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
215 v3f pos_f = getBasePosition();
216 v3f pos_f_old = pos_f;
217 v3f accel_f = v3f(0,0,0);
219 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
220 pos_max_d, box, stepheight, dtime,
221 pos_f, m_speed_f, accel_f);
223 if(send_recommended == false)
226 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
228 setBasePosition(pos_f);
229 m_last_sent_position = pos_f;
231 std::ostringstream os(std::ios::binary);
232 // command (0 = update position)
235 writeV3F1000(os, m_base_position);
236 // create message and add to list
237 ActiveObjectMessage aom(getId(), false, os.str());
238 m_messages_out.push_back(aom);
240 if(m_itemstring_changed)
242 m_itemstring_changed = false;
244 std::ostringstream os(std::ios::binary);
245 // command (1 = update itemstring)
248 os<<serializeString(m_itemstring);
249 // create message and add to list
250 ActiveObjectMessage aom(getId(), false, os.str());
251 m_messages_out.push_back(aom);
255 std::string getClientInitializationData(u16 protocol_version)
257 std::ostringstream os(std::ios::binary);
261 writeV3F1000(os, m_base_position);
263 os<<serializeString(m_itemstring);
267 std::string getStaticData()
269 infostream<<__FUNCTION_NAME<<std::endl;
270 std::ostringstream os(std::ios::binary);
274 os<<serializeString(m_itemstring);
278 ItemStack createItemStack()
281 IItemDefManager *idef = m_env->getGameDef()->idef();
283 item.deSerialize(m_itemstring, idef);
284 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
285 <<"\" -> item=\""<<item.getItemString()<<"\""
289 catch(SerializationError &e)
291 infostream<<__FUNCTION_NAME<<": serialization error: "
292 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
298 const ToolCapabilities *toolcap,
299 ServerActiveObject *puncher,
300 float time_from_last_punch)
302 // Take item into inventory
303 ItemStack item = createItemStack();
304 Inventory *inv = puncher->getInventory();
307 std::string wieldlist = puncher->getWieldList();
308 ItemStack leftover = inv->addItem(wieldlist, item);
309 puncher->setInventoryModified();
316 m_itemstring = leftover.getItemString();
317 m_itemstring_changed = true;
324 bool getCollisionBox(aabb3f *toset) {
330 std::string m_itemstring;
331 bool m_itemstring_changed;
333 v3f m_last_sent_position;
334 IntervalLimiter m_move_interval;
337 // Prototype (registers item for deserialization)
338 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
340 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
341 const std::string itemstring)
343 return new ItemSAO(env, pos, itemstring);
350 // Prototype (registers item for deserialization)
351 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
353 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
354 const std::string &name, const std::string &state):
355 ServerActiveObject(env, pos),
361 m_acceleration(0,0,0),
363 m_properties_sent(true),
365 m_last_sent_position(0,0,0),
366 m_last_sent_velocity(0,0,0),
367 m_last_sent_position_timer(0),
368 m_last_sent_move_precision(0),
369 m_armor_groups_sent(false),
370 m_animation_speed(0),
371 m_animation_blend(0),
372 m_animation_sent(false),
373 m_bone_position_sent(false),
374 m_attachment_parent_id(0),
375 m_attachment_sent(false)
377 // Only register type if no environment supplied
379 ServerActiveObject::registerType(getType(), create);
383 // Initialize something to armor groups
384 m_armor_groups["fleshy"] = 100;
387 LuaEntitySAO::~LuaEntitySAO()
390 ENV_TO_SA(m_env)->luaentity_Remove(m_id);
394 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
396 ServerActiveObject::addedToEnvironment(dtime_s);
398 // Create entity from name
399 m_registered = ENV_TO_SA(m_env)->luaentity_Add(m_id, m_init_name.c_str());
403 ENV_TO_SA(m_env)->luaentity_GetProperties(m_id, &m_prop);
404 // Initialize HP from properties
405 m_hp = m_prop.hp_max;
406 // Activate entity, supplying serialized state
407 ENV_TO_SA(m_env)->luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
411 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
412 const std::string &data)
420 std::istringstream is(data, std::ios::binary);
422 u8 version = readU8(is);
423 // check if version is supported
425 name = deSerializeString(is);
426 state = deSerializeLongString(is);
428 else if(version == 1){
429 name = deSerializeString(is);
430 state = deSerializeLongString(is);
432 velocity = readV3F1000(is);
437 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
438 <<state<<"\")"<<std::endl;
439 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
441 sao->m_velocity = velocity;
446 bool LuaEntitySAO::isAttached()
448 if(!m_attachment_parent_id)
450 // Check if the parent still exists
451 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
457 void LuaEntitySAO::step(float dtime, bool send_recommended)
459 if(!m_properties_sent)
461 m_properties_sent = true;
462 std::string str = getPropertyPacket();
463 // create message and add to list
464 ActiveObjectMessage aom(getId(), true, str);
465 m_messages_out.push_back(aom);
468 // If attached, check that our parent is still there. If it isn't, detach.
469 if(m_attachment_parent_id && !isAttached())
471 m_attachment_parent_id = 0;
472 m_attachment_bone = "";
473 m_attachment_position = v3f(0,0,0);
474 m_attachment_rotation = v3f(0,0,0);
475 sendPosition(false, true);
478 m_last_sent_position_timer += dtime;
480 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
481 // If the object gets detached this comes into effect automatically from the last known origin
484 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
485 m_base_position = pos;
486 m_velocity = v3f(0,0,0);
487 m_acceleration = v3f(0,0,0);
492 core::aabbox3d<f32> box = m_prop.collisionbox;
495 collisionMoveResult moveresult;
496 f32 pos_max_d = BS*0.25; // Distance per iteration
497 f32 stepheight = 0; // Maximum climbable step height
498 v3f p_pos = m_base_position;
499 v3f p_velocity = m_velocity;
500 v3f p_acceleration = m_acceleration;
501 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
502 pos_max_d, box, stepheight, dtime,
503 p_pos, p_velocity, p_acceleration,this);
505 m_base_position = p_pos;
506 m_velocity = p_velocity;
507 m_acceleration = p_acceleration;
509 m_base_position += dtime * m_velocity + 0.5 * dtime
510 * dtime * m_acceleration;
511 m_velocity += dtime * m_acceleration;
516 ENV_TO_SA(m_env)->luaentity_Step(m_id, dtime);
519 if(send_recommended == false)
524 // TODO: force send when acceleration changes enough?
525 float minchange = 0.2*BS;
526 if(m_last_sent_position_timer > 1.0){
528 } else if(m_last_sent_position_timer > 0.2){
531 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
532 move_d += m_last_sent_move_precision;
533 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
534 if(move_d > minchange || vel_d > minchange ||
535 fabs(m_yaw - m_last_sent_yaw) > 1.0){
536 sendPosition(true, false);
540 if(m_armor_groups_sent == false){
541 m_armor_groups_sent = true;
542 std::string str = gob_cmd_update_armor_groups(
544 // create message and add to list
545 ActiveObjectMessage aom(getId(), true, str);
546 m_messages_out.push_back(aom);
549 if(m_animation_sent == false){
550 m_animation_sent = true;
551 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
552 // create message and add to list
553 ActiveObjectMessage aom(getId(), true, str);
554 m_messages_out.push_back(aom);
557 if(m_bone_position_sent == false){
558 m_bone_position_sent = true;
559 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
560 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
561 // create message and add to list
562 ActiveObjectMessage aom(getId(), true, str);
563 m_messages_out.push_back(aom);
567 if(m_attachment_sent == false){
568 m_attachment_sent = true;
569 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
570 // create message and add to list
571 ActiveObjectMessage aom(getId(), true, str);
572 m_messages_out.push_back(aom);
576 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
578 std::ostringstream os(std::ios::binary);
580 if(protocol_version >= 14)
582 writeU8(os, 1); // version
583 os<<serializeString(""); // name
584 writeU8(os, 0); // is_player
585 writeS16(os, getId()); //id
586 writeV3F1000(os, m_base_position);
587 writeF1000(os, m_yaw);
590 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
591 os<<serializeLongString(getPropertyPacket()); // message 1
592 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
593 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
594 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
595 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
597 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
601 writeU8(os, 0); // version
602 os<<serializeString(""); // name
603 writeU8(os, 0); // is_player
604 writeV3F1000(os, m_base_position);
605 writeF1000(os, m_yaw);
607 writeU8(os, 2); // number of messages stuffed in here
608 os<<serializeLongString(getPropertyPacket()); // message 1
609 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
616 std::string LuaEntitySAO::getStaticData()
618 verbosestream<<__FUNCTION_NAME<<std::endl;
619 std::ostringstream os(std::ios::binary);
623 os<<serializeString(m_init_name);
626 std::string state = ENV_TO_SA(m_env)->luaentity_GetStaticdata(m_id);
627 os<<serializeLongString(state);
629 os<<serializeLongString(m_init_state);
634 writeV3F1000(os, m_velocity);
636 writeF1000(os, m_yaw);
640 int LuaEntitySAO::punch(v3f dir,
641 const ToolCapabilities *toolcap,
642 ServerActiveObject *puncher,
643 float time_from_last_punch)
646 // Delete unknown LuaEntities when punched
651 // It's best that attachments cannot be punched
655 ItemStack *punchitem = NULL;
656 ItemStack punchitem_static;
658 punchitem_static = puncher->getWieldedItem();
659 punchitem = &punchitem_static;
662 PunchDamageResult result = getPunchDamage(
666 time_from_last_punch);
670 setHP(getHP() - result.damage);
673 std::string punchername = "nil";
676 punchername = puncher->getDescription();
678 actionstream<<getDescription()<<" punched by "
679 <<punchername<<", damage "<<result.damage
680 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
683 std::string str = gob_cmd_punched(result.damage, getHP());
684 // create message and add to list
685 ActiveObjectMessage aom(getId(), true, str);
686 m_messages_out.push_back(aom);
693 ENV_TO_SA(m_env)->luaentity_Punch(m_id, puncher,
694 time_from_last_punch, toolcap, dir);
699 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
703 // It's best that attachments cannot be clicked
706 ENV_TO_SA(m_env)->luaentity_Rightclick(m_id, clicker);
709 void LuaEntitySAO::setPos(v3f pos)
713 m_base_position = pos;
714 sendPosition(false, true);
717 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
721 m_base_position = pos;
723 sendPosition(true, true);
726 float LuaEntitySAO::getMinimumSavedMovement()
731 std::string LuaEntitySAO::getDescription()
733 std::ostringstream os(std::ios::binary);
734 os<<"LuaEntitySAO at (";
735 os<<(m_base_position.X/BS)<<",";
736 os<<(m_base_position.Y/BS)<<",";
737 os<<(m_base_position.Z/BS);
742 void LuaEntitySAO::setHP(s16 hp)
748 s16 LuaEntitySAO::getHP() const
753 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
755 m_armor_groups = armor_groups;
756 m_armor_groups_sent = false;
759 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
761 m_animation_range = frame_range;
762 m_animation_speed = frame_speed;
763 m_animation_blend = frame_blend;
764 m_animation_sent = false;
767 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
769 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
770 m_bone_position_sent = false;
773 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
775 // Attachments need to be handled on both the server and client.
776 // If we just attach on the server, we can only copy the position of the parent. Attachments
777 // are still sent to clients at an interval so players might see them lagging, plus we can't
778 // read and attach to skeletal bones.
779 // If we just attach on the client, the server still sees the child at its original location.
780 // This breaks some things so we also give the server the most accurate representation
781 // even if players only see the client changes.
783 m_attachment_parent_id = parent_id;
784 m_attachment_bone = bone;
785 m_attachment_position = position;
786 m_attachment_rotation = rotation;
787 m_attachment_sent = false;
790 ObjectProperties* LuaEntitySAO::accessObjectProperties()
795 void LuaEntitySAO::notifyObjectPropertiesModified()
797 m_properties_sent = false;
800 void LuaEntitySAO::setVelocity(v3f velocity)
802 m_velocity = velocity;
805 v3f LuaEntitySAO::getVelocity()
810 void LuaEntitySAO::setAcceleration(v3f acceleration)
812 m_acceleration = acceleration;
815 v3f LuaEntitySAO::getAcceleration()
817 return m_acceleration;
820 void LuaEntitySAO::setYaw(float yaw)
825 float LuaEntitySAO::getYaw()
830 void LuaEntitySAO::setTextureMod(const std::string &mod)
832 std::string str = gob_cmd_set_texture_mod(mod);
833 // create message and add to list
834 ActiveObjectMessage aom(getId(), true, str);
835 m_messages_out.push_back(aom);
838 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
839 bool select_horiz_by_yawpitch)
841 std::string str = gob_cmd_set_sprite(
845 select_horiz_by_yawpitch
847 // create message and add to list
848 ActiveObjectMessage aom(getId(), true, str);
849 m_messages_out.push_back(aom);
852 std::string LuaEntitySAO::getName()
857 std::string LuaEntitySAO::getPropertyPacket()
859 return gob_cmd_set_properties(m_prop);
862 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
864 // If the object is attached client-side, don't waste bandwidth sending its position to clients
868 m_last_sent_move_precision = m_base_position.getDistanceFrom(
869 m_last_sent_position);
870 m_last_sent_position_timer = 0;
871 m_last_sent_yaw = m_yaw;
872 m_last_sent_position = m_base_position;
873 m_last_sent_velocity = m_velocity;
874 //m_last_sent_acceleration = m_acceleration;
876 float update_interval = m_env->getSendRecommendedInterval();
878 std::string str = gob_cmd_update_position(
887 // create message and add to list
888 ActiveObjectMessage aom(getId(), false, str);
889 m_messages_out.push_back(aom);
892 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
895 //update collision box
896 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
897 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
899 toset->MinEdge += m_base_position;
900 toset->MaxEdge += m_base_position;
912 // No prototype, PlayerSAO does not need to be deserialized
914 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
915 const std::set<std::string> &privs, bool is_singleplayer):
916 ServerActiveObject(env_, v3f(0,0,0)),
920 m_last_good_position(0,0,0),
921 m_last_good_position_age(0),
922 m_time_from_last_punch(0),
923 m_nocheat_dig_pos(32767, 32767, 32767),
924 m_nocheat_dig_time(0),
926 m_position_not_sent(false),
927 m_armor_groups_sent(false),
928 m_properties_sent(true),
930 m_is_singleplayer(is_singleplayer),
931 m_animation_sent(false),
932 m_bone_position_sent(false),
933 m_attachment_sent(false),
936 m_inventory_not_sent(false),
937 m_hp_not_sent(false),
938 m_wielded_item_not_sent(false),
939 m_physics_override_speed(1),
940 m_physics_override_jump(1),
941 m_physics_override_gravity(1),
942 m_physics_override_sent(false)
945 assert(m_peer_id != 0);
946 setBasePosition(m_player->getPosition());
947 m_inventory = &m_player->inventory;
948 m_armor_groups["fleshy"] = 100;
950 m_prop.hp_max = PLAYER_MAX_HP;
951 m_prop.physical = false;
953 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
954 // start of default appearance, this should be overwritten by LUA
955 m_prop.visual = "upright_sprite";
956 m_prop.visual_size = v2f(1, 2);
957 m_prop.textures.clear();
958 m_prop.textures.push_back("player.png");
959 m_prop.textures.push_back("player_back.png");
960 m_prop.colors.clear();
961 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
962 m_prop.spritediv = v2s16(1,1);
963 // end of default appearance
964 m_prop.is_visible = true;
965 m_prop.makes_footstep_sound = true;
968 PlayerSAO::~PlayerSAO()
970 if(m_inventory != &m_player->inventory)
975 std::string PlayerSAO::getDescription()
977 return std::string("player ") + m_player->getName();
980 // Called after id has been set and has been inserted in environment
981 void PlayerSAO::addedToEnvironment(u32 dtime_s)
983 ServerActiveObject::addedToEnvironment(dtime_s);
984 ServerActiveObject::setBasePosition(m_player->getPosition());
985 m_player->setPlayerSAO(this);
986 m_player->peer_id = m_peer_id;
987 m_last_good_position = m_player->getPosition();
988 m_last_good_position_age = 0.0;
991 // Called before removing from environment
992 void PlayerSAO::removingFromEnvironment()
994 ServerActiveObject::removingFromEnvironment();
995 if(m_player->getPlayerSAO() == this)
997 m_player->setPlayerSAO(NULL);
998 m_player->peer_id = 0;
1002 bool PlayerSAO::isStaticAllowed() const
1007 bool PlayerSAO::unlimitedTransferDistance() const
1009 return g_settings->getBool("unlimited_player_transfer_distance");
1012 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1014 std::ostringstream os(std::ios::binary);
1016 if(protocol_version >= 15)
1018 writeU8(os, 1); // version
1019 os<<serializeString(m_player->getName()); // name
1020 writeU8(os, 1); // is_player
1021 writeS16(os, getId()); //id
1022 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1023 writeF1000(os, m_player->getYaw());
1024 writeS16(os, getHP());
1026 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1027 os<<serializeLongString(getPropertyPacket()); // message 1
1028 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1029 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1030 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1031 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1033 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1034 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1038 writeU8(os, 0); // version
1039 os<<serializeString(m_player->getName()); // name
1040 writeU8(os, 1); // is_player
1041 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1042 writeF1000(os, m_player->getYaw());
1043 writeS16(os, getHP());
1044 writeU8(os, 2); // number of messages stuffed in here
1045 os<<serializeLongString(getPropertyPacket()); // message 1
1046 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1053 std::string PlayerSAO::getStaticData()
1059 bool PlayerSAO::isAttached()
1061 if(!m_attachment_parent_id)
1063 // Check if the parent still exists
1064 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1070 void PlayerSAO::step(float dtime, bool send_recommended)
1072 if(!m_properties_sent)
1074 m_properties_sent = true;
1075 std::string str = getPropertyPacket();
1076 // create message and add to list
1077 ActiveObjectMessage aom(getId(), true, str);
1078 m_messages_out.push_back(aom);
1081 // If attached, check that our parent is still there. If it isn't, detach.
1082 if(m_attachment_parent_id && !isAttached())
1084 m_attachment_parent_id = 0;
1085 m_attachment_bone = "";
1086 m_attachment_position = v3f(0,0,0);
1087 m_attachment_rotation = v3f(0,0,0);
1088 m_player->setPosition(m_last_good_position);
1092 m_time_from_last_punch += dtime;
1093 m_nocheat_dig_time += dtime;
1095 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1096 // If the object gets detached this comes into effect automatically from the last known origin
1099 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1100 m_last_good_position = pos;
1101 m_last_good_position_age = 0;
1102 m_player->setPosition(pos);
1106 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1108 m_last_good_position = m_player->getPosition();
1109 m_last_good_position_age = 0;
1114 Check player movements
1116 NOTE: Actually the server should handle player physics like the
1117 client does and compare player's position to what is calculated
1118 on our side. This is required when eg. players fly due to an
1119 explosion. Altough a node-based alternative might be possible
1120 too, and much more lightweight.
1123 float player_max_speed = 0;
1124 float player_max_speed_up = 0;
1125 if(m_privs.count("fast") != 0){
1127 player_max_speed = BS * 20;
1128 player_max_speed_up = BS * 20;
1131 player_max_speed = BS * 4.0;
1132 player_max_speed_up = BS * 4.0;
1135 player_max_speed *= 2.5;
1136 player_max_speed_up *= 2.5;
1138 m_last_good_position_age += dtime;
1139 if(m_last_good_position_age >= 1.0){
1140 float age = m_last_good_position_age;
1141 v3f diff = (m_player->getPosition() - m_last_good_position);
1142 float d_vert = diff.Y;
1144 float d_horiz = diff.getLength();
1145 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1146 <<(d_horiz/age)<<std::endl;*/
1147 if(d_horiz <= age * player_max_speed &&
1148 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1149 m_last_good_position = m_player->getPosition();
1151 actionstream<<"Player "<<m_player->getName()
1152 <<" moved too fast; resetting position"
1154 m_player->setPosition(m_last_good_position);
1157 m_last_good_position_age = 0;
1162 if(send_recommended == false)
1165 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1166 if(m_position_not_sent && !isAttached())
1168 m_position_not_sent = false;
1169 float update_interval = m_env->getSendRecommendedInterval();
1171 if(isAttached()) // Just in case we ever do send attachment position too
1172 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1174 pos = m_player->getPosition() + v3f(0,BS*1,0);
1175 std::string str = gob_cmd_update_position(
1184 // create message and add to list
1185 ActiveObjectMessage aom(getId(), false, str);
1186 m_messages_out.push_back(aom);
1189 if(m_wielded_item_not_sent)
1191 m_wielded_item_not_sent = false;
1192 // GenericCAO has no special way to show this
1195 if(m_armor_groups_sent == false){
1196 m_armor_groups_sent = true;
1197 std::string str = gob_cmd_update_armor_groups(
1199 // create message and add to list
1200 ActiveObjectMessage aom(getId(), true, str);
1201 m_messages_out.push_back(aom);
1204 if(m_physics_override_sent == false){
1205 m_physics_override_sent = true;
1206 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1207 // create message and add to list
1208 ActiveObjectMessage aom(getId(), true, str);
1209 m_messages_out.push_back(aom);
1212 if(m_animation_sent == false){
1213 m_animation_sent = true;
1214 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1215 // create message and add to list
1216 ActiveObjectMessage aom(getId(), true, str);
1217 m_messages_out.push_back(aom);
1220 if(m_bone_position_sent == false){
1221 m_bone_position_sent = true;
1222 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1223 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1224 // create message and add to list
1225 ActiveObjectMessage aom(getId(), true, str);
1226 m_messages_out.push_back(aom);
1230 if(m_attachment_sent == false){
1231 m_attachment_sent = true;
1232 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1233 // create message and add to list
1234 ActiveObjectMessage aom(getId(), true, str);
1235 m_messages_out.push_back(aom);
1239 void PlayerSAO::setBasePosition(const v3f &position)
1241 // This needs to be ran for attachments too
1242 ServerActiveObject::setBasePosition(position);
1243 m_position_not_sent = true;
1246 void PlayerSAO::setPos(v3f pos)
1250 m_player->setPosition(pos);
1251 // Movement caused by this command is always valid
1252 m_last_good_position = pos;
1253 m_last_good_position_age = 0;
1254 // Force position change on client
1258 void PlayerSAO::moveTo(v3f pos, bool continuous)
1262 m_player->setPosition(pos);
1263 // Movement caused by this command is always valid
1264 m_last_good_position = pos;
1265 m_last_good_position_age = 0;
1266 // Force position change on client
1270 void PlayerSAO::setYaw(float yaw)
1272 m_player->setYaw(yaw);
1273 // Force change on client
1277 void PlayerSAO::setPitch(float pitch)
1279 m_player->setPitch(pitch);
1280 // Force change on client
1284 int PlayerSAO::punch(v3f dir,
1285 const ToolCapabilities *toolcap,
1286 ServerActiveObject *puncher,
1287 float time_from_last_punch)
1289 // It's best that attachments cannot be punched
1296 // No effect if PvP disabled
1297 if(g_settings->getBool("enable_pvp") == false){
1298 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1299 std::string str = gob_cmd_punched(0, getHP());
1300 // create message and add to list
1301 ActiveObjectMessage aom(getId(), true, str);
1302 m_messages_out.push_back(aom);
1307 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1308 time_from_last_punch);
1310 std::string punchername = "nil";
1313 punchername = puncher->getDescription();
1315 actionstream<<"Player "<<m_player->getName()<<" punched by "
1316 <<punchername<<", damage "<<hitparams.hp
1319 setHP(getHP() - hitparams.hp);
1321 if(hitparams.hp != 0)
1323 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1324 // create message and add to list
1325 ActiveObjectMessage aom(getId(), true, str);
1326 m_messages_out.push_back(aom);
1329 return hitparams.wear;
1332 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1336 s16 PlayerSAO::getHP() const
1338 return m_player->hp;
1341 void PlayerSAO::setHP(s16 hp)
1343 s16 oldhp = m_player->hp;
1347 else if(hp > PLAYER_MAX_HP)
1350 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1352 m_hp_not_sent = true; // fix wrong prediction on client
1359 m_hp_not_sent = true;
1361 // On death or reincarnation send an active object message
1362 if((hp == 0) != (oldhp == 0))
1364 // Will send new is_visible value based on (getHP()!=0)
1365 m_properties_sent = false;
1367 std::string str = gob_cmd_punched(0, getHP());
1368 ActiveObjectMessage aom(getId(), true, str);
1369 m_messages_out.push_back(aom);
1373 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1375 m_armor_groups = armor_groups;
1376 m_armor_groups_sent = false;
1379 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1381 // store these so they can be updated to clients
1382 m_animation_range = frame_range;
1383 m_animation_speed = frame_speed;
1384 m_animation_blend = frame_blend;
1385 m_animation_sent = false;
1388 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1390 // store these so they can be updated to clients
1391 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1392 m_bone_position_sent = false;
1395 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1397 // Attachments need to be handled on both the server and client.
1398 // If we just attach on the server, we can only copy the position of the parent. Attachments
1399 // are still sent to clients at an interval so players might see them lagging, plus we can't
1400 // read and attach to skeletal bones.
1401 // If we just attach on the client, the server still sees the child at its original location.
1402 // This breaks some things so we also give the server the most accurate representation
1403 // even if players only see the client changes.
1405 m_attachment_parent_id = parent_id;
1406 m_attachment_bone = bone;
1407 m_attachment_position = position;
1408 m_attachment_rotation = rotation;
1409 m_attachment_sent = false;
1412 ObjectProperties* PlayerSAO::accessObjectProperties()
1417 void PlayerSAO::notifyObjectPropertiesModified()
1419 m_properties_sent = false;
1422 Inventory* PlayerSAO::getInventory()
1426 const Inventory* PlayerSAO::getInventory() const
1431 InventoryLocation PlayerSAO::getInventoryLocation() const
1433 InventoryLocation loc;
1434 loc.setPlayer(m_player->getName());
1438 void PlayerSAO::setInventoryModified()
1440 m_inventory_not_sent = true;
1443 std::string PlayerSAO::getWieldList() const
1448 int PlayerSAO::getWieldIndex() const
1450 return m_wield_index;
1453 void PlayerSAO::setWieldIndex(int i)
1455 if(i != m_wield_index)
1458 m_wielded_item_not_sent = true;
1462 void PlayerSAO::disconnected()
1466 if(m_player->getPlayerSAO() == this)
1468 m_player->setPlayerSAO(NULL);
1469 m_player->peer_id = 0;
1473 std::string PlayerSAO::getPropertyPacket()
1475 m_prop.is_visible = (true);
1476 return gob_cmd_set_properties(m_prop);
1479 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1480 //update collision box
1481 *toset = m_player->getCollisionbox();
1483 toset->MinEdge += m_base_position;
1484 toset->MaxEdge += m_base_position;