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_breath_not_sent(false),
939 m_wielded_item_not_sent(false),
940 m_physics_override_speed(1),
941 m_physics_override_jump(1),
942 m_physics_override_gravity(1),
943 m_physics_override_sent(false)
946 assert(m_peer_id != 0);
947 setBasePosition(m_player->getPosition());
948 m_inventory = &m_player->inventory;
949 m_armor_groups["fleshy"] = 100;
951 m_prop.hp_max = PLAYER_MAX_HP;
952 m_prop.physical = false;
954 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
955 // start of default appearance, this should be overwritten by LUA
956 m_prop.visual = "upright_sprite";
957 m_prop.visual_size = v2f(1, 2);
958 m_prop.textures.clear();
959 m_prop.textures.push_back("player.png");
960 m_prop.textures.push_back("player_back.png");
961 m_prop.colors.clear();
962 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
963 m_prop.spritediv = v2s16(1,1);
964 // end of default appearance
965 m_prop.is_visible = true;
966 m_prop.makes_footstep_sound = true;
969 PlayerSAO::~PlayerSAO()
971 if(m_inventory != &m_player->inventory)
976 std::string PlayerSAO::getDescription()
978 return std::string("player ") + m_player->getName();
981 // Called after id has been set and has been inserted in environment
982 void PlayerSAO::addedToEnvironment(u32 dtime_s)
984 ServerActiveObject::addedToEnvironment(dtime_s);
985 ServerActiveObject::setBasePosition(m_player->getPosition());
986 m_player->setPlayerSAO(this);
987 m_player->peer_id = m_peer_id;
988 m_last_good_position = m_player->getPosition();
989 m_last_good_position_age = 0.0;
992 // Called before removing from environment
993 void PlayerSAO::removingFromEnvironment()
995 ServerActiveObject::removingFromEnvironment();
996 if(m_player->getPlayerSAO() == this)
998 m_player->setPlayerSAO(NULL);
999 m_player->peer_id = 0;
1003 bool PlayerSAO::isStaticAllowed() const
1008 bool PlayerSAO::unlimitedTransferDistance() const
1010 return g_settings->getBool("unlimited_player_transfer_distance");
1013 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1015 std::ostringstream os(std::ios::binary);
1017 if(protocol_version >= 15)
1019 writeU8(os, 1); // version
1020 os<<serializeString(m_player->getName()); // name
1021 writeU8(os, 1); // is_player
1022 writeS16(os, getId()); //id
1023 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1024 writeF1000(os, m_player->getYaw());
1025 writeS16(os, getHP());
1027 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1028 os<<serializeLongString(getPropertyPacket()); // message 1
1029 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1030 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1031 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1032 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1034 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1035 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1039 writeU8(os, 0); // version
1040 os<<serializeString(m_player->getName()); // name
1041 writeU8(os, 1); // is_player
1042 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1043 writeF1000(os, m_player->getYaw());
1044 writeS16(os, getHP());
1045 writeU8(os, 2); // number of messages stuffed in here
1046 os<<serializeLongString(getPropertyPacket()); // message 1
1047 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1054 std::string PlayerSAO::getStaticData()
1060 bool PlayerSAO::isAttached()
1062 if(!m_attachment_parent_id)
1064 // Check if the parent still exists
1065 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1071 void PlayerSAO::step(float dtime, bool send_recommended)
1073 if(!m_properties_sent)
1075 m_properties_sent = true;
1076 std::string str = getPropertyPacket();
1077 // create message and add to list
1078 ActiveObjectMessage aom(getId(), true, str);
1079 m_messages_out.push_back(aom);
1082 // If attached, check that our parent is still there. If it isn't, detach.
1083 if(m_attachment_parent_id && !isAttached())
1085 m_attachment_parent_id = 0;
1086 m_attachment_bone = "";
1087 m_attachment_position = v3f(0,0,0);
1088 m_attachment_rotation = v3f(0,0,0);
1089 m_player->setPosition(m_last_good_position);
1093 m_time_from_last_punch += dtime;
1094 m_nocheat_dig_time += dtime;
1096 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1097 // If the object gets detached this comes into effect automatically from the last known origin
1100 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1101 m_last_good_position = pos;
1102 m_last_good_position_age = 0;
1103 m_player->setPosition(pos);
1107 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1109 m_last_good_position = m_player->getPosition();
1110 m_last_good_position_age = 0;
1115 Check player movements
1117 NOTE: Actually the server should handle player physics like the
1118 client does and compare player's position to what is calculated
1119 on our side. This is required when eg. players fly due to an
1120 explosion. Altough a node-based alternative might be possible
1121 too, and much more lightweight.
1124 float player_max_speed = 0;
1125 float player_max_speed_up = 0;
1126 if(m_privs.count("fast") != 0){
1128 player_max_speed = BS * 20;
1129 player_max_speed_up = BS * 20;
1132 player_max_speed = BS * 4.0;
1133 player_max_speed_up = BS * 4.0;
1136 player_max_speed *= 2.5;
1137 player_max_speed_up *= 2.5;
1139 m_last_good_position_age += dtime;
1140 if(m_last_good_position_age >= 1.0){
1141 float age = m_last_good_position_age;
1142 v3f diff = (m_player->getPosition() - m_last_good_position);
1143 float d_vert = diff.Y;
1145 float d_horiz = diff.getLength();
1146 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1147 <<(d_horiz/age)<<std::endl;*/
1148 if(d_horiz <= age * player_max_speed &&
1149 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1150 m_last_good_position = m_player->getPosition();
1152 actionstream<<"Player "<<m_player->getName()
1153 <<" moved too fast; resetting position"
1155 m_player->setPosition(m_last_good_position);
1158 m_last_good_position_age = 0;
1163 if(send_recommended == false)
1166 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1167 if(m_position_not_sent && !isAttached())
1169 m_position_not_sent = false;
1170 float update_interval = m_env->getSendRecommendedInterval();
1172 if(isAttached()) // Just in case we ever do send attachment position too
1173 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1175 pos = m_player->getPosition() + v3f(0,BS*1,0);
1176 std::string str = gob_cmd_update_position(
1185 // create message and add to list
1186 ActiveObjectMessage aom(getId(), false, str);
1187 m_messages_out.push_back(aom);
1190 if(m_wielded_item_not_sent)
1192 m_wielded_item_not_sent = false;
1193 // GenericCAO has no special way to show this
1196 if(m_armor_groups_sent == false){
1197 m_armor_groups_sent = true;
1198 std::string str = gob_cmd_update_armor_groups(
1200 // create message and add to list
1201 ActiveObjectMessage aom(getId(), true, str);
1202 m_messages_out.push_back(aom);
1205 if(m_physics_override_sent == false){
1206 m_physics_override_sent = true;
1207 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1208 // create message and add to list
1209 ActiveObjectMessage aom(getId(), true, str);
1210 m_messages_out.push_back(aom);
1213 if(m_animation_sent == false){
1214 m_animation_sent = true;
1215 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1216 // create message and add to list
1217 ActiveObjectMessage aom(getId(), true, str);
1218 m_messages_out.push_back(aom);
1221 if(m_bone_position_sent == false){
1222 m_bone_position_sent = true;
1223 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1224 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1225 // create message and add to list
1226 ActiveObjectMessage aom(getId(), true, str);
1227 m_messages_out.push_back(aom);
1231 if(m_attachment_sent == false){
1232 m_attachment_sent = true;
1233 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1234 // create message and add to list
1235 ActiveObjectMessage aom(getId(), true, str);
1236 m_messages_out.push_back(aom);
1240 void PlayerSAO::setBasePosition(const v3f &position)
1242 // This needs to be ran for attachments too
1243 ServerActiveObject::setBasePosition(position);
1244 m_position_not_sent = true;
1247 void PlayerSAO::setPos(v3f pos)
1251 m_player->setPosition(pos);
1252 // Movement caused by this command is always valid
1253 m_last_good_position = pos;
1254 m_last_good_position_age = 0;
1255 // Force position change on client
1259 void PlayerSAO::moveTo(v3f pos, bool continuous)
1263 m_player->setPosition(pos);
1264 // Movement caused by this command is always valid
1265 m_last_good_position = pos;
1266 m_last_good_position_age = 0;
1267 // Force position change on client
1271 void PlayerSAO::setYaw(float yaw)
1273 m_player->setYaw(yaw);
1274 // Force change on client
1278 void PlayerSAO::setPitch(float pitch)
1280 m_player->setPitch(pitch);
1281 // Force change on client
1285 int PlayerSAO::punch(v3f dir,
1286 const ToolCapabilities *toolcap,
1287 ServerActiveObject *puncher,
1288 float time_from_last_punch)
1290 // It's best that attachments cannot be punched
1297 // No effect if PvP disabled
1298 if(g_settings->getBool("enable_pvp") == false){
1299 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1300 std::string str = gob_cmd_punched(0, getHP());
1301 // create message and add to list
1302 ActiveObjectMessage aom(getId(), true, str);
1303 m_messages_out.push_back(aom);
1308 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1309 time_from_last_punch);
1311 std::string punchername = "nil";
1314 punchername = puncher->getDescription();
1316 actionstream<<"Player "<<m_player->getName()<<" punched by "
1317 <<punchername<<", damage "<<hitparams.hp
1320 setHP(getHP() - hitparams.hp);
1322 if(hitparams.hp != 0)
1324 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1325 // create message and add to list
1326 ActiveObjectMessage aom(getId(), true, str);
1327 m_messages_out.push_back(aom);
1330 return hitparams.wear;
1333 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1337 s16 PlayerSAO::getHP() const
1339 return m_player->hp;
1342 void PlayerSAO::setHP(s16 hp)
1344 s16 oldhp = m_player->hp;
1348 else if(hp > PLAYER_MAX_HP)
1351 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1353 m_hp_not_sent = true; // fix wrong prediction on client
1360 m_hp_not_sent = true;
1362 // On death or reincarnation send an active object message
1363 if((hp == 0) != (oldhp == 0))
1365 // Will send new is_visible value based on (getHP()!=0)
1366 m_properties_sent = false;
1368 std::string str = gob_cmd_punched(0, getHP());
1369 ActiveObjectMessage aom(getId(), true, str);
1370 m_messages_out.push_back(aom);
1374 u16 PlayerSAO::getBreath() const
1376 return m_player->getBreath();
1379 void PlayerSAO::setBreath(u16 breath)
1381 m_player->setBreath(breath);
1384 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1386 m_armor_groups = armor_groups;
1387 m_armor_groups_sent = false;
1390 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1392 // store these so they can be updated to clients
1393 m_animation_range = frame_range;
1394 m_animation_speed = frame_speed;
1395 m_animation_blend = frame_blend;
1396 m_animation_sent = false;
1399 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1401 // store these so they can be updated to clients
1402 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1403 m_bone_position_sent = false;
1406 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1408 // Attachments need to be handled on both the server and client.
1409 // If we just attach on the server, we can only copy the position of the parent. Attachments
1410 // are still sent to clients at an interval so players might see them lagging, plus we can't
1411 // read and attach to skeletal bones.
1412 // If we just attach on the client, the server still sees the child at its original location.
1413 // This breaks some things so we also give the server the most accurate representation
1414 // even if players only see the client changes.
1416 m_attachment_parent_id = parent_id;
1417 m_attachment_bone = bone;
1418 m_attachment_position = position;
1419 m_attachment_rotation = rotation;
1420 m_attachment_sent = false;
1423 ObjectProperties* PlayerSAO::accessObjectProperties()
1428 void PlayerSAO::notifyObjectPropertiesModified()
1430 m_properties_sent = false;
1433 Inventory* PlayerSAO::getInventory()
1437 const Inventory* PlayerSAO::getInventory() const
1442 InventoryLocation PlayerSAO::getInventoryLocation() const
1444 InventoryLocation loc;
1445 loc.setPlayer(m_player->getName());
1449 void PlayerSAO::setInventoryModified()
1451 m_inventory_not_sent = true;
1454 std::string PlayerSAO::getWieldList() const
1459 int PlayerSAO::getWieldIndex() const
1461 return m_wield_index;
1464 void PlayerSAO::setWieldIndex(int i)
1466 if(i != m_wield_index)
1469 m_wielded_item_not_sent = true;
1473 void PlayerSAO::disconnected()
1477 if(m_player->getPlayerSAO() == this)
1479 m_player->setPlayerSAO(NULL);
1480 m_player->peer_id = 0;
1484 std::string PlayerSAO::getPropertyPacket()
1486 m_prop.is_visible = (true);
1487 return gob_cmd_set_properties(m_prop);
1490 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1491 //update collision box
1492 *toset = m_player->getCollisionbox();
1494 toset->MinEdge += m_base_position;
1495 toset->MaxEdge += m_base_position;