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);
677 std::string punchername = "nil";
680 punchername = puncher->getDescription();
682 actionstream<<getDescription()<<" punched by "
683 <<punchername<<", damage "<<result.damage
684 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
687 std::string str = gob_cmd_punched(result.damage, getHP());
688 // create message and add to list
689 ActiveObjectMessage aom(getId(), true, str);
690 m_messages_out.push_back(aom);
697 lua_State *L = m_env->getLua();
698 scriptapi_luaentity_punch(L, m_id, puncher,
699 time_from_last_punch, toolcap, dir);
704 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
708 // It's best that attachments cannot be clicked
711 lua_State *L = m_env->getLua();
712 scriptapi_luaentity_rightclick(L, m_id, clicker);
715 void LuaEntitySAO::setPos(v3f pos)
719 m_base_position = pos;
720 sendPosition(false, true);
723 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
727 m_base_position = pos;
729 sendPosition(true, true);
732 float LuaEntitySAO::getMinimumSavedMovement()
737 std::string LuaEntitySAO::getDescription()
739 std::ostringstream os(std::ios::binary);
740 os<<"LuaEntitySAO at (";
741 os<<(m_base_position.X/BS)<<",";
742 os<<(m_base_position.Y/BS)<<",";
743 os<<(m_base_position.Z/BS);
748 void LuaEntitySAO::setHP(s16 hp)
754 s16 LuaEntitySAO::getHP() const
759 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
761 m_armor_groups = armor_groups;
762 m_armor_groups_sent = false;
765 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
767 m_animation_range = frame_range;
768 m_animation_speed = frame_speed;
769 m_animation_blend = frame_blend;
770 m_animation_sent = false;
773 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
775 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
776 m_bone_position_sent = false;
779 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
781 // Attachments need to be handled on both the server and client.
782 // If we just attach on the server, we can only copy the position of the parent. Attachments
783 // are still sent to clients at an interval so players might see them lagging, plus we can't
784 // read and attach to skeletal bones.
785 // If we just attach on the client, the server still sees the child at its original location.
786 // This breaks some things so we also give the server the most accurate representation
787 // even if players only see the client changes.
789 m_attachment_parent_id = parent_id;
790 m_attachment_bone = bone;
791 m_attachment_position = position;
792 m_attachment_rotation = rotation;
793 m_attachment_sent = false;
796 ObjectProperties* LuaEntitySAO::accessObjectProperties()
801 void LuaEntitySAO::notifyObjectPropertiesModified()
803 m_properties_sent = false;
806 void LuaEntitySAO::setVelocity(v3f velocity)
808 m_velocity = velocity;
811 v3f LuaEntitySAO::getVelocity()
816 void LuaEntitySAO::setAcceleration(v3f acceleration)
818 m_acceleration = acceleration;
821 v3f LuaEntitySAO::getAcceleration()
823 return m_acceleration;
826 void LuaEntitySAO::setYaw(float yaw)
831 float LuaEntitySAO::getYaw()
836 void LuaEntitySAO::setTextureMod(const std::string &mod)
838 std::string str = gob_cmd_set_texture_mod(mod);
839 // create message and add to list
840 ActiveObjectMessage aom(getId(), true, str);
841 m_messages_out.push_back(aom);
844 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
845 bool select_horiz_by_yawpitch)
847 std::string str = gob_cmd_set_sprite(
851 select_horiz_by_yawpitch
853 // create message and add to list
854 ActiveObjectMessage aom(getId(), true, str);
855 m_messages_out.push_back(aom);
858 std::string LuaEntitySAO::getName()
863 std::string LuaEntitySAO::getPropertyPacket()
865 return gob_cmd_set_properties(m_prop);
868 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
870 // If the object is attached client-side, don't waste bandwidth sending its position to clients
874 m_last_sent_move_precision = m_base_position.getDistanceFrom(
875 m_last_sent_position);
876 m_last_sent_position_timer = 0;
877 m_last_sent_yaw = m_yaw;
878 m_last_sent_position = m_base_position;
879 m_last_sent_velocity = m_velocity;
880 //m_last_sent_acceleration = m_acceleration;
882 float update_interval = m_env->getSendRecommendedInterval();
884 std::string str = gob_cmd_update_position(
893 // create message and add to list
894 ActiveObjectMessage aom(getId(), false, str);
895 m_messages_out.push_back(aom);
898 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
901 //update collision box
902 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
903 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
905 toset->MinEdge += m_base_position;
906 toset->MaxEdge += m_base_position;
918 // No prototype, PlayerSAO does not need to be deserialized
920 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
921 const std::set<std::string> &privs, bool is_singleplayer):
922 ServerActiveObject(env_, v3f(0,0,0)),
926 m_last_good_position(0,0,0),
927 m_last_good_position_age(0),
928 m_time_from_last_punch(0),
929 m_nocheat_dig_pos(32767, 32767, 32767),
930 m_nocheat_dig_time(0),
932 m_position_not_sent(false),
933 m_armor_groups_sent(false),
934 m_properties_sent(true),
936 m_is_singleplayer(is_singleplayer),
937 m_animation_sent(false),
938 m_bone_position_sent(false),
939 m_attachment_sent(false),
942 m_inventory_not_sent(false),
943 m_hp_not_sent(false),
944 m_wielded_item_not_sent(false),
945 m_physics_override_speed(1),
946 m_physics_override_jump(1),
947 m_physics_override_gravity(1),
948 m_physics_override_sent(false)
951 assert(m_peer_id != 0);
952 setBasePosition(m_player->getPosition());
953 m_inventory = &m_player->inventory;
954 m_armor_groups["fleshy"] = 100;
956 m_prop.hp_max = PLAYER_MAX_HP;
957 m_prop.physical = false;
959 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
960 // start of default appearance, this should be overwritten by LUA
961 m_prop.visual = "upright_sprite";
962 m_prop.visual_size = v2f(1, 2);
963 m_prop.textures.clear();
964 m_prop.textures.push_back("player.png");
965 m_prop.textures.push_back("player_back.png");
966 m_prop.colors.clear();
967 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
968 m_prop.spritediv = v2s16(1,1);
969 // end of default appearance
970 m_prop.is_visible = true;
971 m_prop.makes_footstep_sound = true;
974 PlayerSAO::~PlayerSAO()
976 if(m_inventory != &m_player->inventory)
981 std::string PlayerSAO::getDescription()
983 return std::string("player ") + m_player->getName();
986 // Called after id has been set and has been inserted in environment
987 void PlayerSAO::addedToEnvironment(u32 dtime_s)
989 ServerActiveObject::addedToEnvironment(dtime_s);
990 ServerActiveObject::setBasePosition(m_player->getPosition());
991 m_player->setPlayerSAO(this);
992 m_player->peer_id = m_peer_id;
993 m_last_good_position = m_player->getPosition();
994 m_last_good_position_age = 0.0;
997 // Called before removing from environment
998 void PlayerSAO::removingFromEnvironment()
1000 ServerActiveObject::removingFromEnvironment();
1001 if(m_player->getPlayerSAO() == this)
1003 m_player->setPlayerSAO(NULL);
1004 m_player->peer_id = 0;
1008 bool PlayerSAO::isStaticAllowed() const
1013 bool PlayerSAO::unlimitedTransferDistance() const
1015 return g_settings->getBool("unlimited_player_transfer_distance");
1018 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1020 std::ostringstream os(std::ios::binary);
1022 if(protocol_version >= 15)
1024 writeU8(os, 1); // version
1025 os<<serializeString(m_player->getName()); // name
1026 writeU8(os, 1); // is_player
1027 writeS16(os, getId()); //id
1028 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1029 writeF1000(os, m_player->getYaw());
1030 writeS16(os, getHP());
1032 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1033 os<<serializeLongString(getPropertyPacket()); // message 1
1034 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1035 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1036 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1037 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1039 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1040 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1044 writeU8(os, 0); // version
1045 os<<serializeString(m_player->getName()); // name
1046 writeU8(os, 1); // is_player
1047 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1048 writeF1000(os, m_player->getYaw());
1049 writeS16(os, getHP());
1050 writeU8(os, 2); // number of messages stuffed in here
1051 os<<serializeLongString(getPropertyPacket()); // message 1
1052 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1059 std::string PlayerSAO::getStaticData()
1065 bool PlayerSAO::isAttached()
1067 if(!m_attachment_parent_id)
1069 // Check if the parent still exists
1070 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1076 void PlayerSAO::step(float dtime, bool send_recommended)
1078 if(!m_properties_sent)
1080 m_properties_sent = true;
1081 std::string str = getPropertyPacket();
1082 // create message and add to list
1083 ActiveObjectMessage aom(getId(), true, str);
1084 m_messages_out.push_back(aom);
1087 // If attached, check that our parent is still there. If it isn't, detach.
1088 if(m_attachment_parent_id && !isAttached())
1090 m_attachment_parent_id = 0;
1091 m_attachment_bone = "";
1092 m_attachment_position = v3f(0,0,0);
1093 m_attachment_rotation = v3f(0,0,0);
1094 m_player->setPosition(m_last_good_position);
1098 m_time_from_last_punch += dtime;
1099 m_nocheat_dig_time += dtime;
1101 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1102 // If the object gets detached this comes into effect automatically from the last known origin
1105 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1106 m_last_good_position = pos;
1107 m_last_good_position_age = 0;
1108 m_player->setPosition(pos);
1112 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1114 m_last_good_position = m_player->getPosition();
1115 m_last_good_position_age = 0;
1120 Check player movements
1122 NOTE: Actually the server should handle player physics like the
1123 client does and compare player's position to what is calculated
1124 on our side. This is required when eg. players fly due to an
1125 explosion. Altough a node-based alternative might be possible
1126 too, and much more lightweight.
1129 float player_max_speed = 0;
1130 float player_max_speed_up = 0;
1131 if(m_privs.count("fast") != 0){
1133 player_max_speed = BS * 20;
1134 player_max_speed_up = BS * 20;
1137 player_max_speed = BS * 4.0;
1138 player_max_speed_up = BS * 4.0;
1141 player_max_speed *= 2.5;
1142 player_max_speed_up *= 2.5;
1144 m_last_good_position_age += dtime;
1145 if(m_last_good_position_age >= 1.0){
1146 float age = m_last_good_position_age;
1147 v3f diff = (m_player->getPosition() - m_last_good_position);
1148 float d_vert = diff.Y;
1150 float d_horiz = diff.getLength();
1151 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1152 <<(d_horiz/age)<<std::endl;*/
1153 if(d_horiz <= age * player_max_speed &&
1154 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1155 m_last_good_position = m_player->getPosition();
1157 actionstream<<"Player "<<m_player->getName()
1158 <<" moved too fast; resetting position"
1160 m_player->setPosition(m_last_good_position);
1163 m_last_good_position_age = 0;
1168 if(send_recommended == false)
1171 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1172 if(m_position_not_sent && !isAttached())
1174 m_position_not_sent = false;
1175 float update_interval = m_env->getSendRecommendedInterval();
1177 if(isAttached()) // Just in case we ever do send attachment position too
1178 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1180 pos = m_player->getPosition() + v3f(0,BS*1,0);
1181 std::string str = gob_cmd_update_position(
1190 // create message and add to list
1191 ActiveObjectMessage aom(getId(), false, str);
1192 m_messages_out.push_back(aom);
1195 if(m_wielded_item_not_sent)
1197 m_wielded_item_not_sent = false;
1198 // GenericCAO has no special way to show this
1201 if(m_armor_groups_sent == false){
1202 m_armor_groups_sent = true;
1203 std::string str = gob_cmd_update_armor_groups(
1205 // create message and add to list
1206 ActiveObjectMessage aom(getId(), true, str);
1207 m_messages_out.push_back(aom);
1210 if(m_physics_override_sent == false){
1211 m_physics_override_sent = true;
1212 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1213 // create message and add to list
1214 ActiveObjectMessage aom(getId(), true, str);
1215 m_messages_out.push_back(aom);
1218 if(m_animation_sent == false){
1219 m_animation_sent = true;
1220 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1221 // create message and add to list
1222 ActiveObjectMessage aom(getId(), true, str);
1223 m_messages_out.push_back(aom);
1226 if(m_bone_position_sent == false){
1227 m_bone_position_sent = true;
1228 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1229 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1230 // create message and add to list
1231 ActiveObjectMessage aom(getId(), true, str);
1232 m_messages_out.push_back(aom);
1236 if(m_attachment_sent == false){
1237 m_attachment_sent = true;
1238 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1239 // create message and add to list
1240 ActiveObjectMessage aom(getId(), true, str);
1241 m_messages_out.push_back(aom);
1245 void PlayerSAO::setBasePosition(const v3f &position)
1247 // This needs to be ran for attachments too
1248 ServerActiveObject::setBasePosition(position);
1249 m_position_not_sent = true;
1252 void PlayerSAO::setPos(v3f pos)
1256 m_player->setPosition(pos);
1257 // Movement caused by this command is always valid
1258 m_last_good_position = pos;
1259 m_last_good_position_age = 0;
1260 // Force position change on client
1264 void PlayerSAO::moveTo(v3f pos, bool continuous)
1268 m_player->setPosition(pos);
1269 // Movement caused by this command is always valid
1270 m_last_good_position = pos;
1271 m_last_good_position_age = 0;
1272 // Force position change on client
1276 void PlayerSAO::setYaw(float yaw)
1278 m_player->setYaw(yaw);
1279 // Force change on client
1283 void PlayerSAO::setPitch(float pitch)
1285 m_player->setPitch(pitch);
1286 // Force change on client
1290 int PlayerSAO::punch(v3f dir,
1291 const ToolCapabilities *toolcap,
1292 ServerActiveObject *puncher,
1293 float time_from_last_punch)
1295 // It's best that attachments cannot be punched
1302 // No effect if PvP disabled
1303 if(g_settings->getBool("enable_pvp") == false){
1304 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1305 std::string str = gob_cmd_punched(0, getHP());
1306 // create message and add to list
1307 ActiveObjectMessage aom(getId(), true, str);
1308 m_messages_out.push_back(aom);
1313 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1314 time_from_last_punch);
1316 std::string punchername = "nil";
1319 punchername = puncher->getDescription();
1321 actionstream<<"Player "<<m_player->getName()<<" punched by "
1322 <<punchername<<", damage "<<hitparams.hp
1325 setHP(getHP() - hitparams.hp);
1327 if(hitparams.hp != 0)
1329 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1330 // create message and add to list
1331 ActiveObjectMessage aom(getId(), true, str);
1332 m_messages_out.push_back(aom);
1335 return hitparams.wear;
1338 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1342 s16 PlayerSAO::getHP() const
1344 return m_player->hp;
1347 void PlayerSAO::setHP(s16 hp)
1349 s16 oldhp = m_player->hp;
1353 else if(hp > PLAYER_MAX_HP)
1356 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1358 m_hp_not_sent = true; // fix wrong prediction on client
1365 m_hp_not_sent = true;
1367 // On death or reincarnation send an active object message
1368 if((hp == 0) != (oldhp == 0))
1370 // Will send new is_visible value based on (getHP()!=0)
1371 m_properties_sent = false;
1373 std::string str = gob_cmd_punched(0, getHP());
1374 ActiveObjectMessage aom(getId(), true, str);
1375 m_messages_out.push_back(aom);
1379 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1381 m_armor_groups = armor_groups;
1382 m_armor_groups_sent = false;
1385 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1387 // store these so they can be updated to clients
1388 m_animation_range = frame_range;
1389 m_animation_speed = frame_speed;
1390 m_animation_blend = frame_blend;
1391 m_animation_sent = false;
1394 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1396 // store these so they can be updated to clients
1397 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1398 m_bone_position_sent = false;
1401 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1403 // Attachments need to be handled on both the server and client.
1404 // If we just attach on the server, we can only copy the position of the parent. Attachments
1405 // are still sent to clients at an interval so players might see them lagging, plus we can't
1406 // read and attach to skeletal bones.
1407 // If we just attach on the client, the server still sees the child at its original location.
1408 // This breaks some things so we also give the server the most accurate representation
1409 // even if players only see the client changes.
1411 m_attachment_parent_id = parent_id;
1412 m_attachment_bone = bone;
1413 m_attachment_position = position;
1414 m_attachment_rotation = rotation;
1415 m_attachment_sent = false;
1418 ObjectProperties* PlayerSAO::accessObjectProperties()
1423 void PlayerSAO::notifyObjectPropertiesModified()
1425 m_properties_sent = false;
1428 Inventory* PlayerSAO::getInventory()
1432 const Inventory* PlayerSAO::getInventory() const
1437 InventoryLocation PlayerSAO::getInventoryLocation() const
1439 InventoryLocation loc;
1440 loc.setPlayer(m_player->getName());
1444 void PlayerSAO::setInventoryModified()
1446 m_inventory_not_sent = true;
1449 std::string PlayerSAO::getWieldList() const
1454 int PlayerSAO::getWieldIndex() const
1456 return m_wield_index;
1459 void PlayerSAO::setWieldIndex(int i)
1461 if(i != m_wield_index)
1464 m_wielded_item_not_sent = true;
1468 void PlayerSAO::disconnected()
1472 if(m_player->getPlayerSAO() == this)
1474 m_player->setPlayerSAO(NULL);
1475 m_player->peer_id = 0;
1479 std::string PlayerSAO::getPropertyPacket()
1481 m_prop.is_visible = (true);
1482 return gob_cmd_set_properties(m_prop);
1485 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1486 //update collision box
1487 *toset = m_player->getCollisionbox();
1489 toset->MinEdge += m_base_position;
1490 toset->MaxEdge += m_base_position;