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 "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 lua_State *L = m_env->getLua();
391 scriptapi_luaentity_rm(L, m_id);
395 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
397 ServerActiveObject::addedToEnvironment(dtime_s);
399 // Create entity from name
400 lua_State *L = m_env->getLua();
401 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
405 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
406 // Initialize HP from properties
407 m_hp = m_prop.hp_max;
408 // Activate entity, supplying serialized state
409 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
413 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
414 const std::string &data)
422 std::istringstream is(data, std::ios::binary);
424 u8 version = readU8(is);
425 // check if version is supported
427 name = deSerializeString(is);
428 state = deSerializeLongString(is);
430 else if(version == 1){
431 name = deSerializeString(is);
432 state = deSerializeLongString(is);
434 velocity = readV3F1000(is);
439 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
440 <<state<<"\")"<<std::endl;
441 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
443 sao->m_velocity = velocity;
448 bool LuaEntitySAO::isAttached()
450 if(!m_attachment_parent_id)
452 // Check if the parent still exists
453 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
459 void LuaEntitySAO::step(float dtime, bool send_recommended)
461 if(!m_properties_sent)
463 m_properties_sent = true;
464 std::string str = getPropertyPacket();
465 // create message and add to list
466 ActiveObjectMessage aom(getId(), true, str);
467 m_messages_out.push_back(aom);
470 // If attached, check that our parent is still there. If it isn't, detach.
471 if(m_attachment_parent_id && !isAttached())
473 m_attachment_parent_id = 0;
474 m_attachment_bone = "";
475 m_attachment_position = v3f(0,0,0);
476 m_attachment_rotation = v3f(0,0,0);
477 sendPosition(false, true);
480 m_last_sent_position_timer += dtime;
482 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
483 // If the object gets detached this comes into effect automatically from the last known origin
486 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
487 m_base_position = pos;
488 m_velocity = v3f(0,0,0);
489 m_acceleration = v3f(0,0,0);
494 core::aabbox3d<f32> box = m_prop.collisionbox;
497 collisionMoveResult moveresult;
498 f32 pos_max_d = BS*0.25; // Distance per iteration
499 f32 stepheight = 0; // Maximum climbable step height
500 v3f p_pos = m_base_position;
501 v3f p_velocity = m_velocity;
502 v3f p_acceleration = m_acceleration;
503 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
504 pos_max_d, box, stepheight, dtime,
505 p_pos, p_velocity, p_acceleration,this);
507 m_base_position = p_pos;
508 m_velocity = p_velocity;
509 m_acceleration = p_acceleration;
511 m_base_position += dtime * m_velocity + 0.5 * dtime
512 * dtime * m_acceleration;
513 m_velocity += dtime * m_acceleration;
518 lua_State *L = m_env->getLua();
519 scriptapi_luaentity_step(L, m_id, dtime);
522 if(send_recommended == false)
527 // TODO: force send when acceleration changes enough?
528 float minchange = 0.2*BS;
529 if(m_last_sent_position_timer > 1.0){
531 } else if(m_last_sent_position_timer > 0.2){
534 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
535 move_d += m_last_sent_move_precision;
536 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
537 if(move_d > minchange || vel_d > minchange ||
538 fabs(m_yaw - m_last_sent_yaw) > 1.0){
539 sendPosition(true, false);
543 if(m_armor_groups_sent == false){
544 m_armor_groups_sent = true;
545 std::string str = gob_cmd_update_armor_groups(
547 // create message and add to list
548 ActiveObjectMessage aom(getId(), true, str);
549 m_messages_out.push_back(aom);
552 if(m_animation_sent == false){
553 m_animation_sent = true;
554 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
555 // create message and add to list
556 ActiveObjectMessage aom(getId(), true, str);
557 m_messages_out.push_back(aom);
560 if(m_bone_position_sent == false){
561 m_bone_position_sent = true;
562 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
563 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
564 // create message and add to list
565 ActiveObjectMessage aom(getId(), true, str);
566 m_messages_out.push_back(aom);
570 if(m_attachment_sent == false){
571 m_attachment_sent = true;
572 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
573 // create message and add to list
574 ActiveObjectMessage aom(getId(), true, str);
575 m_messages_out.push_back(aom);
579 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
581 std::ostringstream os(std::ios::binary);
583 if(protocol_version >= 14)
585 writeU8(os, 1); // version
586 os<<serializeString(""); // name
587 writeU8(os, 0); // is_player
588 writeS16(os, getId()); //id
589 writeV3F1000(os, m_base_position);
590 writeF1000(os, m_yaw);
593 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
594 os<<serializeLongString(getPropertyPacket()); // message 1
595 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
596 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
597 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
598 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
600 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
604 writeU8(os, 0); // version
605 os<<serializeString(""); // name
606 writeU8(os, 0); // is_player
607 writeV3F1000(os, m_base_position);
608 writeF1000(os, m_yaw);
610 writeU8(os, 2); // number of messages stuffed in here
611 os<<serializeLongString(getPropertyPacket()); // message 1
612 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
619 std::string LuaEntitySAO::getStaticData()
621 verbosestream<<__FUNCTION_NAME<<std::endl;
622 std::ostringstream os(std::ios::binary);
626 os<<serializeString(m_init_name);
629 lua_State *L = m_env->getLua();
630 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
631 os<<serializeLongString(state);
633 os<<serializeLongString(m_init_state);
638 writeV3F1000(os, m_velocity);
640 writeF1000(os, m_yaw);
644 int LuaEntitySAO::punch(v3f dir,
645 const ToolCapabilities *toolcap,
646 ServerActiveObject *puncher,
647 float time_from_last_punch)
650 // Delete unknown LuaEntities when punched
655 // It's best that attachments cannot be punched
659 ItemStack *punchitem = NULL;
660 ItemStack punchitem_static;
662 punchitem_static = puncher->getWieldedItem();
663 punchitem = &punchitem_static;
666 PunchDamageResult result = getPunchDamage(
670 time_from_last_punch);
674 setHP(getHP() - result.damage);
676 actionstream<<getDescription()<<" punched by "
677 <<puncher->getDescription()<<", damage "<<result.damage
678 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
681 std::string str = gob_cmd_punched(result.damage, getHP());
682 // create message and add to list
683 ActiveObjectMessage aom(getId(), true, str);
684 m_messages_out.push_back(aom);
691 lua_State *L = m_env->getLua();
692 scriptapi_luaentity_punch(L, m_id, puncher,
693 time_from_last_punch, toolcap, dir);
698 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
702 // It's best that attachments cannot be clicked
705 lua_State *L = m_env->getLua();
706 scriptapi_luaentity_rightclick(L, 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 actionstream<<"Player "<<m_player->getName()<<" punched by "
1311 <<puncher->getDescription()<<", damage "<<hitparams.hp
1314 setHP(getHP() - hitparams.hp);
1316 if(hitparams.hp != 0)
1318 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1319 // create message and add to list
1320 ActiveObjectMessage aom(getId(), true, str);
1321 m_messages_out.push_back(aom);
1324 return hitparams.wear;
1327 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1331 s16 PlayerSAO::getHP() const
1333 return m_player->hp;
1336 void PlayerSAO::setHP(s16 hp)
1338 s16 oldhp = m_player->hp;
1342 else if(hp > PLAYER_MAX_HP)
1345 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1347 m_hp_not_sent = true; // fix wrong prediction on client
1354 m_hp_not_sent = true;
1356 // On death or reincarnation send an active object message
1357 if((hp == 0) != (oldhp == 0))
1359 // Will send new is_visible value based on (getHP()!=0)
1360 m_properties_sent = false;
1362 std::string str = gob_cmd_punched(0, getHP());
1363 ActiveObjectMessage aom(getId(), true, str);
1364 m_messages_out.push_back(aom);
1368 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1370 m_armor_groups = armor_groups;
1371 m_armor_groups_sent = false;
1374 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1376 // store these so they can be updated to clients
1377 m_animation_range = frame_range;
1378 m_animation_speed = frame_speed;
1379 m_animation_blend = frame_blend;
1380 m_animation_sent = false;
1383 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1385 // store these so they can be updated to clients
1386 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1387 m_bone_position_sent = false;
1390 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1392 // Attachments need to be handled on both the server and client.
1393 // If we just attach on the server, we can only copy the position of the parent. Attachments
1394 // are still sent to clients at an interval so players might see them lagging, plus we can't
1395 // read and attach to skeletal bones.
1396 // If we just attach on the client, the server still sees the child at its original location.
1397 // This breaks some things so we also give the server the most accurate representation
1398 // even if players only see the client changes.
1400 m_attachment_parent_id = parent_id;
1401 m_attachment_bone = bone;
1402 m_attachment_position = position;
1403 m_attachment_rotation = rotation;
1404 m_attachment_sent = false;
1407 ObjectProperties* PlayerSAO::accessObjectProperties()
1412 void PlayerSAO::notifyObjectPropertiesModified()
1414 m_properties_sent = false;
1417 Inventory* PlayerSAO::getInventory()
1421 const Inventory* PlayerSAO::getInventory() const
1426 InventoryLocation PlayerSAO::getInventoryLocation() const
1428 InventoryLocation loc;
1429 loc.setPlayer(m_player->getName());
1433 void PlayerSAO::setInventoryModified()
1435 m_inventory_not_sent = true;
1438 std::string PlayerSAO::getWieldList() const
1443 int PlayerSAO::getWieldIndex() const
1445 return m_wield_index;
1448 void PlayerSAO::setWieldIndex(int i)
1450 if(i != m_wield_index)
1453 m_wielded_item_not_sent = true;
1457 void PlayerSAO::disconnected()
1461 if(m_player->getPlayerSAO() == this)
1463 m_player->setPlayerSAO(NULL);
1464 m_player->peer_id = 0;
1468 std::string PlayerSAO::getPropertyPacket()
1470 m_prop.is_visible = (true);
1471 return gob_cmd_set_properties(m_prop);
1474 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1475 //player collision handling is already done clientside no need to do it twice