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 "scripting_game.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
33 #include "util/mathconstants.h"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class DummyLoadSAO : public ServerActiveObject
44 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
45 ServerActiveObject(env, pos)
47 ServerActiveObject::registerType(type, create);
49 // Pretend to be the test object (to fool the client)
51 { return ACTIVEOBJECT_TYPE_TEST; }
52 // And never save to disk
53 bool isStaticAllowed() const
56 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
57 const std::string &data)
59 return new DummyLoadSAO(env, pos, 0);
62 void step(float dtime, bool send_recommended)
65 infostream<<"DummyLoadSAO step"<<std::endl;
68 bool getCollisionBox(aabb3f *toset) {
72 bool collideWithObjects() {
79 // Prototype (registers item for deserialization)
80 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
81 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
82 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
83 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
89 class TestSAO : public ServerActiveObject
92 TestSAO(ServerEnvironment *env, v3f pos):
93 ServerActiveObject(env, pos),
97 ServerActiveObject::registerType(getType(), create);
100 { return ACTIVEOBJECT_TYPE_TEST; }
102 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
103 const std::string &data)
105 return new TestSAO(env, pos);
108 void step(float dtime, bool send_recommended)
117 m_base_position.Y += dtime * BS * 2;
118 if(m_base_position.Y > 8*BS)
119 m_base_position.Y = 2*BS;
121 if(send_recommended == false)
131 data += itos(0); // 0 = position
133 data += itos(m_base_position.X);
135 data += itos(m_base_position.Y);
137 data += itos(m_base_position.Z);
139 ActiveObjectMessage aom(getId(), false, data);
140 m_messages_out.push_back(aom);
144 bool getCollisionBox(aabb3f *toset) {
148 bool collideWithObjects() {
157 // Prototype (registers item for deserialization)
158 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
163 DEPRECATED: New dropped items are implemented in Lua; see
164 builtin/item_entity.lua.
167 class ItemSAO : public ServerActiveObject
171 { return ACTIVEOBJECT_TYPE_ITEM; }
173 float getMinimumSavedMovement()
176 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
177 const std::string &data)
179 std::istringstream is(data, std::ios::binary);
184 // check if version is supported
187 std::string itemstring = deSerializeString(is);
188 infostream<<"create(): Creating item \""
189 <<itemstring<<"\""<<std::endl;
190 return new ItemSAO(env, pos, itemstring);
193 ItemSAO(ServerEnvironment *env, v3f pos,
194 const std::string itemstring):
195 ServerActiveObject(env, pos),
196 m_itemstring(itemstring),
197 m_itemstring_changed(false),
199 m_last_sent_position(0,0,0)
201 ServerActiveObject::registerType(getType(), create);
204 void step(float dtime, bool send_recommended)
206 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
210 const float interval = 0.2;
211 if(m_move_interval.step(dtime, interval)==false)
215 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
216 collisionMoveResult moveresult;
218 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
219 // Maximum movement without glitches
220 f32 pos_max_d = BS*0.25;
222 if(m_speed_f.getLength()*dtime > pos_max_d)
223 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
224 v3f pos_f = getBasePosition();
225 v3f pos_f_old = pos_f;
226 v3f accel_f = v3f(0,0,0);
228 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
229 pos_max_d, box, stepheight, dtime,
230 pos_f, m_speed_f, accel_f);
232 if(send_recommended == false)
235 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
237 setBasePosition(pos_f);
238 m_last_sent_position = pos_f;
240 std::ostringstream os(std::ios::binary);
241 // command (0 = update position)
244 writeV3F1000(os, m_base_position);
245 // create message and add to list
246 ActiveObjectMessage aom(getId(), false, os.str());
247 m_messages_out.push_back(aom);
249 if(m_itemstring_changed)
251 m_itemstring_changed = false;
253 std::ostringstream os(std::ios::binary);
254 // command (1 = update itemstring)
257 os<<serializeString(m_itemstring);
258 // create message and add to list
259 ActiveObjectMessage aom(getId(), false, os.str());
260 m_messages_out.push_back(aom);
264 std::string getClientInitializationData(u16 protocol_version)
266 std::ostringstream os(std::ios::binary);
270 writeV3F1000(os, m_base_position);
272 os<<serializeString(m_itemstring);
276 std::string getStaticData()
278 infostream<<__FUNCTION_NAME<<std::endl;
279 std::ostringstream os(std::ios::binary);
283 os<<serializeString(m_itemstring);
287 ItemStack createItemStack()
290 IItemDefManager *idef = m_env->getGameDef()->idef();
292 item.deSerialize(m_itemstring, idef);
293 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
294 <<"\" -> item=\""<<item.getItemString()<<"\""
298 catch(SerializationError &e)
300 infostream<<__FUNCTION_NAME<<": serialization error: "
301 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
307 const ToolCapabilities *toolcap,
308 ServerActiveObject *puncher,
309 float time_from_last_punch)
311 // Take item into inventory
312 ItemStack item = createItemStack();
313 Inventory *inv = puncher->getInventory();
316 std::string wieldlist = puncher->getWieldList();
317 ItemStack leftover = inv->addItem(wieldlist, item);
318 puncher->setInventoryModified();
325 m_itemstring = leftover.getItemString();
326 m_itemstring_changed = true;
333 bool getCollisionBox(aabb3f *toset) {
337 bool collideWithObjects() {
342 std::string m_itemstring;
343 bool m_itemstring_changed;
345 v3f m_last_sent_position;
346 IntervalLimiter m_move_interval;
349 // Prototype (registers item for deserialization)
350 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
352 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
353 const std::string itemstring)
355 return new ItemSAO(env, pos, itemstring);
362 // Prototype (registers item for deserialization)
363 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
365 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
366 const std::string &name, const std::string &state):
367 ServerActiveObject(env, pos),
373 m_acceleration(0,0,0),
375 m_properties_sent(true),
377 m_last_sent_position(0,0,0),
378 m_last_sent_velocity(0,0,0),
379 m_last_sent_position_timer(0),
380 m_last_sent_move_precision(0),
381 m_armor_groups_sent(false),
382 m_animation_speed(0),
383 m_animation_blend(0),
384 m_animation_sent(false),
385 m_bone_position_sent(false),
386 m_attachment_parent_id(0),
387 m_attachment_sent(false)
389 // Only register type if no environment supplied
391 ServerActiveObject::registerType(getType(), create);
395 // Initialize something to armor groups
396 m_armor_groups["fleshy"] = 100;
399 LuaEntitySAO::~LuaEntitySAO()
402 m_env->getScriptIface()->luaentity_Remove(m_id);
406 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
408 ServerActiveObject::addedToEnvironment(dtime_s);
410 // Create entity from name
411 m_registered = m_env->getScriptIface()->
412 luaentity_Add(m_id, m_init_name.c_str());
416 m_env->getScriptIface()->
417 luaentity_GetProperties(m_id, &m_prop);
418 // Initialize HP from properties
419 m_hp = m_prop.hp_max;
420 // Activate entity, supplying serialized state
421 m_env->getScriptIface()->
422 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
426 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
427 const std::string &data)
435 std::istringstream is(data, std::ios::binary);
437 u8 version = readU8(is);
438 // check if version is supported
440 name = deSerializeString(is);
441 state = deSerializeLongString(is);
443 else if(version == 1){
444 name = deSerializeString(is);
445 state = deSerializeLongString(is);
447 velocity = readV3F1000(is);
452 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
453 <<state<<"\")"<<std::endl;
454 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
456 sao->m_velocity = velocity;
461 bool LuaEntitySAO::isAttached()
463 if(!m_attachment_parent_id)
465 // Check if the parent still exists
466 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
472 void LuaEntitySAO::step(float dtime, bool send_recommended)
474 if(!m_properties_sent)
476 m_properties_sent = true;
477 std::string str = getPropertyPacket();
478 // create message and add to list
479 ActiveObjectMessage aom(getId(), true, str);
480 m_messages_out.push_back(aom);
483 // If attached, check that our parent is still there. If it isn't, detach.
484 if(m_attachment_parent_id && !isAttached())
486 m_attachment_parent_id = 0;
487 m_attachment_bone = "";
488 m_attachment_position = v3f(0,0,0);
489 m_attachment_rotation = v3f(0,0,0);
490 sendPosition(false, true);
493 m_last_sent_position_timer += dtime;
495 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
496 // If the object gets detached this comes into effect automatically from the last known origin
499 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
500 m_base_position = pos;
501 m_velocity = v3f(0,0,0);
502 m_acceleration = v3f(0,0,0);
507 core::aabbox3d<f32> box = m_prop.collisionbox;
510 collisionMoveResult moveresult;
511 f32 pos_max_d = BS*0.25; // Distance per iteration
512 v3f p_pos = m_base_position;
513 v3f p_velocity = m_velocity;
514 v3f p_acceleration = m_acceleration;
515 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
516 pos_max_d, box, m_prop.stepheight, dtime,
517 p_pos, p_velocity, p_acceleration,
518 this, m_prop.collideWithObjects);
521 m_base_position = p_pos;
522 m_velocity = p_velocity;
523 m_acceleration = p_acceleration;
525 m_base_position += dtime * m_velocity + 0.5 * dtime
526 * dtime * m_acceleration;
527 m_velocity += dtime * m_acceleration;
530 if((m_prop.automatic_face_movement_dir) &&
531 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
532 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
537 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
540 if(send_recommended == false)
545 // TODO: force send when acceleration changes enough?
546 float minchange = 0.2*BS;
547 if(m_last_sent_position_timer > 1.0){
549 } else if(m_last_sent_position_timer > 0.2){
552 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
553 move_d += m_last_sent_move_precision;
554 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
555 if(move_d > minchange || vel_d > minchange ||
556 fabs(m_yaw - m_last_sent_yaw) > 1.0){
557 sendPosition(true, false);
561 if(m_armor_groups_sent == false){
562 m_armor_groups_sent = true;
563 std::string str = gob_cmd_update_armor_groups(
565 // create message and add to list
566 ActiveObjectMessage aom(getId(), true, str);
567 m_messages_out.push_back(aom);
570 if(m_animation_sent == false){
571 m_animation_sent = true;
572 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
573 // create message and add to list
574 ActiveObjectMessage aom(getId(), true, str);
575 m_messages_out.push_back(aom);
578 if(m_bone_position_sent == false){
579 m_bone_position_sent = true;
580 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
581 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
582 // create message and add to list
583 ActiveObjectMessage aom(getId(), true, str);
584 m_messages_out.push_back(aom);
588 if(m_attachment_sent == false){
589 m_attachment_sent = true;
590 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
591 // create message and add to list
592 ActiveObjectMessage aom(getId(), true, str);
593 m_messages_out.push_back(aom);
597 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
599 std::ostringstream os(std::ios::binary);
601 if(protocol_version >= 14)
603 writeU8(os, 1); // version
604 os<<serializeString(""); // name
605 writeU8(os, 0); // is_player
606 writeS16(os, getId()); //id
607 writeV3F1000(os, m_base_position);
608 writeF1000(os, m_yaw);
611 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
612 os<<serializeLongString(getPropertyPacket()); // message 1
613 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
614 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
615 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
616 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
618 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
622 writeU8(os, 0); // version
623 os<<serializeString(""); // name
624 writeU8(os, 0); // is_player
625 writeV3F1000(os, m_base_position);
626 writeF1000(os, m_yaw);
628 writeU8(os, 2); // number of messages stuffed in here
629 os<<serializeLongString(getPropertyPacket()); // message 1
630 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
637 std::string LuaEntitySAO::getStaticData()
639 verbosestream<<__FUNCTION_NAME<<std::endl;
640 std::ostringstream os(std::ios::binary);
644 os<<serializeString(m_init_name);
647 std::string state = m_env->getScriptIface()->
648 luaentity_GetStaticdata(m_id);
649 os<<serializeLongString(state);
651 os<<serializeLongString(m_init_state);
656 writeV3F1000(os, m_velocity);
658 writeF1000(os, m_yaw);
662 int LuaEntitySAO::punch(v3f dir,
663 const ToolCapabilities *toolcap,
664 ServerActiveObject *puncher,
665 float time_from_last_punch)
668 // Delete unknown LuaEntities when punched
673 // It's best that attachments cannot be punched
677 ItemStack *punchitem = NULL;
678 ItemStack punchitem_static;
680 punchitem_static = puncher->getWieldedItem();
681 punchitem = &punchitem_static;
684 PunchDamageResult result = getPunchDamage(
688 time_from_last_punch);
692 setHP(getHP() - result.damage);
695 std::string punchername = "nil";
698 punchername = puncher->getDescription();
700 actionstream<<getDescription()<<" punched by "
701 <<punchername<<", damage "<<result.damage
702 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
705 std::string str = gob_cmd_punched(result.damage, getHP());
706 // create message and add to list
707 ActiveObjectMessage aom(getId(), true, str);
708 m_messages_out.push_back(aom);
715 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
716 time_from_last_punch, toolcap, dir);
721 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
725 // It's best that attachments cannot be clicked
728 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
731 void LuaEntitySAO::setPos(v3f pos)
735 m_base_position = pos;
736 sendPosition(false, true);
739 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
743 m_base_position = pos;
745 sendPosition(true, true);
748 float LuaEntitySAO::getMinimumSavedMovement()
753 std::string LuaEntitySAO::getDescription()
755 std::ostringstream os(std::ios::binary);
756 os<<"LuaEntitySAO at (";
757 os<<(m_base_position.X/BS)<<",";
758 os<<(m_base_position.Y/BS)<<",";
759 os<<(m_base_position.Z/BS);
764 void LuaEntitySAO::setHP(s16 hp)
770 s16 LuaEntitySAO::getHP() const
775 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
777 m_armor_groups = armor_groups;
778 m_armor_groups_sent = false;
781 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
783 m_animation_range = frame_range;
784 m_animation_speed = frame_speed;
785 m_animation_blend = frame_blend;
786 m_animation_sent = false;
789 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
791 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
792 m_bone_position_sent = false;
795 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
797 // Attachments need to be handled on both the server and client.
798 // If we just attach on the server, we can only copy the position of the parent. Attachments
799 // are still sent to clients at an interval so players might see them lagging, plus we can't
800 // read and attach to skeletal bones.
801 // If we just attach on the client, the server still sees the child at its original location.
802 // This breaks some things so we also give the server the most accurate representation
803 // even if players only see the client changes.
805 m_attachment_parent_id = parent_id;
806 m_attachment_bone = bone;
807 m_attachment_position = position;
808 m_attachment_rotation = rotation;
809 m_attachment_sent = false;
812 ObjectProperties* LuaEntitySAO::accessObjectProperties()
817 void LuaEntitySAO::notifyObjectPropertiesModified()
819 m_properties_sent = false;
822 void LuaEntitySAO::setVelocity(v3f velocity)
824 m_velocity = velocity;
827 v3f LuaEntitySAO::getVelocity()
832 void LuaEntitySAO::setAcceleration(v3f acceleration)
834 m_acceleration = acceleration;
837 v3f LuaEntitySAO::getAcceleration()
839 return m_acceleration;
842 void LuaEntitySAO::setYaw(float yaw)
847 float LuaEntitySAO::getYaw()
852 void LuaEntitySAO::setTextureMod(const std::string &mod)
854 std::string str = gob_cmd_set_texture_mod(mod);
855 // create message and add to list
856 ActiveObjectMessage aom(getId(), true, str);
857 m_messages_out.push_back(aom);
860 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
861 bool select_horiz_by_yawpitch)
863 std::string str = gob_cmd_set_sprite(
867 select_horiz_by_yawpitch
869 // create message and add to list
870 ActiveObjectMessage aom(getId(), true, str);
871 m_messages_out.push_back(aom);
874 std::string LuaEntitySAO::getName()
879 std::string LuaEntitySAO::getPropertyPacket()
881 return gob_cmd_set_properties(m_prop);
884 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
886 // If the object is attached client-side, don't waste bandwidth sending its position to clients
890 m_last_sent_move_precision = m_base_position.getDistanceFrom(
891 m_last_sent_position);
892 m_last_sent_position_timer = 0;
893 m_last_sent_yaw = m_yaw;
894 m_last_sent_position = m_base_position;
895 m_last_sent_velocity = m_velocity;
896 //m_last_sent_acceleration = m_acceleration;
898 float update_interval = m_env->getSendRecommendedInterval();
900 std::string str = gob_cmd_update_position(
909 // create message and add to list
910 ActiveObjectMessage aom(getId(), false, str);
911 m_messages_out.push_back(aom);
914 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
917 //update collision box
918 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
919 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
921 toset->MinEdge += m_base_position;
922 toset->MaxEdge += m_base_position;
930 bool LuaEntitySAO::collideWithObjects(){
931 return m_prop.collideWithObjects;
938 // No prototype, PlayerSAO does not need to be deserialized
940 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
941 const std::set<std::string> &privs, bool is_singleplayer):
942 ServerActiveObject(env_, v3f(0,0,0)),
947 m_last_good_position(0,0,0),
948 m_time_from_last_punch(0),
949 m_nocheat_dig_pos(32767, 32767, 32767),
950 m_nocheat_dig_time(0),
952 m_position_not_sent(false),
953 m_armor_groups_sent(false),
954 m_properties_sent(true),
956 m_is_singleplayer(is_singleplayer),
957 m_animation_speed(0),
958 m_animation_blend(0),
959 m_animation_sent(false),
960 m_bone_position_sent(false),
961 m_attachment_parent_id(0),
962 m_attachment_sent(false),
965 m_inventory_not_sent(false),
966 m_hp_not_sent(false),
967 m_breath_not_sent(false),
968 m_wielded_item_not_sent(false),
969 m_physics_override_speed(1),
970 m_physics_override_jump(1),
971 m_physics_override_gravity(1),
972 m_physics_override_sent(false)
975 assert(m_peer_id != 0);
976 setBasePosition(m_player->getPosition());
977 m_inventory = &m_player->inventory;
978 m_armor_groups["fleshy"] = 100;
980 m_prop.hp_max = PLAYER_MAX_HP;
981 m_prop.physical = false;
983 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
984 // start of default appearance, this should be overwritten by LUA
985 m_prop.visual = "upright_sprite";
986 m_prop.visual_size = v2f(1, 2);
987 m_prop.textures.clear();
988 m_prop.textures.push_back("player.png");
989 m_prop.textures.push_back("player_back.png");
990 m_prop.colors.clear();
991 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
992 m_prop.spritediv = v2s16(1,1);
993 // end of default appearance
994 m_prop.is_visible = true;
995 m_prop.makes_footstep_sound = true;
998 PlayerSAO::~PlayerSAO()
1000 if(m_inventory != &m_player->inventory)
1005 std::string PlayerSAO::getDescription()
1007 return std::string("player ") + m_player->getName();
1010 // Called after id has been set and has been inserted in environment
1011 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1013 ServerActiveObject::addedToEnvironment(dtime_s);
1014 ServerActiveObject::setBasePosition(m_player->getPosition());
1015 m_player->setPlayerSAO(this);
1016 m_player->peer_id = m_peer_id;
1017 m_last_good_position = m_player->getPosition();
1020 // Called before removing from environment
1021 void PlayerSAO::removingFromEnvironment()
1023 ServerActiveObject::removingFromEnvironment();
1024 if(m_player->getPlayerSAO() == this)
1026 m_player->setPlayerSAO(NULL);
1027 m_player->peer_id = 0;
1031 bool PlayerSAO::isStaticAllowed() const
1036 bool PlayerSAO::unlimitedTransferDistance() const
1038 return g_settings->getBool("unlimited_player_transfer_distance");
1041 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1043 std::ostringstream os(std::ios::binary);
1045 if(protocol_version >= 15)
1047 writeU8(os, 1); // version
1048 os<<serializeString(m_player->getName()); // name
1049 writeU8(os, 1); // is_player
1050 writeS16(os, getId()); //id
1051 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1052 writeF1000(os, m_player->getYaw());
1053 writeS16(os, getHP());
1055 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1056 os<<serializeLongString(getPropertyPacket()); // message 1
1057 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1058 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1059 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1060 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1062 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1063 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1067 writeU8(os, 0); // version
1068 os<<serializeString(m_player->getName()); // name
1069 writeU8(os, 1); // is_player
1070 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1071 writeF1000(os, m_player->getYaw());
1072 writeS16(os, getHP());
1073 writeU8(os, 2); // number of messages stuffed in here
1074 os<<serializeLongString(getPropertyPacket()); // message 1
1075 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1082 std::string PlayerSAO::getStaticData()
1088 bool PlayerSAO::isAttached()
1090 if(!m_attachment_parent_id)
1092 // Check if the parent still exists
1093 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1099 void PlayerSAO::step(float dtime, bool send_recommended)
1101 if(!m_properties_sent)
1103 m_properties_sent = true;
1104 std::string str = getPropertyPacket();
1105 // create message and add to list
1106 ActiveObjectMessage aom(getId(), true, str);
1107 m_messages_out.push_back(aom);
1110 // If attached, check that our parent is still there. If it isn't, detach.
1111 if(m_attachment_parent_id && !isAttached())
1113 m_attachment_parent_id = 0;
1114 m_attachment_bone = "";
1115 m_attachment_position = v3f(0,0,0);
1116 m_attachment_rotation = v3f(0,0,0);
1117 m_player->setPosition(m_last_good_position);
1121 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1123 // Set lag pool maximums based on estimated lag
1124 const float LAG_POOL_MIN = 5.0;
1125 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1126 if(lag_pool_max < LAG_POOL_MIN)
1127 lag_pool_max = LAG_POOL_MIN;
1128 m_dig_pool.setMax(lag_pool_max);
1129 m_move_pool.setMax(lag_pool_max);
1131 // Increment cheat prevention timers
1132 m_dig_pool.add(dtime);
1133 m_move_pool.add(dtime);
1134 m_time_from_last_punch += dtime;
1135 m_nocheat_dig_time += dtime;
1137 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1138 // If the object gets detached this comes into effect automatically from the last known origin
1141 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1142 m_last_good_position = pos;
1143 m_player->setPosition(pos);
1146 if(send_recommended == false)
1149 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1150 if(m_position_not_sent && !isAttached())
1152 m_position_not_sent = false;
1153 float update_interval = m_env->getSendRecommendedInterval();
1155 if(isAttached()) // Just in case we ever do send attachment position too
1156 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1158 pos = m_player->getPosition() + v3f(0,BS*1,0);
1159 std::string str = gob_cmd_update_position(
1168 // create message and add to list
1169 ActiveObjectMessage aom(getId(), false, str);
1170 m_messages_out.push_back(aom);
1173 if(m_wielded_item_not_sent)
1175 m_wielded_item_not_sent = false;
1176 // GenericCAO has no special way to show this
1179 if(m_armor_groups_sent == false){
1180 m_armor_groups_sent = true;
1181 std::string str = gob_cmd_update_armor_groups(
1183 // create message and add to list
1184 ActiveObjectMessage aom(getId(), true, str);
1185 m_messages_out.push_back(aom);
1188 if(m_physics_override_sent == false){
1189 m_physics_override_sent = true;
1190 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1191 // create message and add to list
1192 ActiveObjectMessage aom(getId(), true, str);
1193 m_messages_out.push_back(aom);
1196 if(m_animation_sent == false){
1197 m_animation_sent = true;
1198 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1199 // create message and add to list
1200 ActiveObjectMessage aom(getId(), true, str);
1201 m_messages_out.push_back(aom);
1204 if(m_bone_position_sent == false){
1205 m_bone_position_sent = true;
1206 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1207 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1208 // create message and add to list
1209 ActiveObjectMessage aom(getId(), true, str);
1210 m_messages_out.push_back(aom);
1214 if(m_attachment_sent == false){
1215 m_attachment_sent = true;
1216 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1217 // create message and add to list
1218 ActiveObjectMessage aom(getId(), true, str);
1219 m_messages_out.push_back(aom);
1223 void PlayerSAO::setBasePosition(const v3f &position)
1225 // This needs to be ran for attachments too
1226 ServerActiveObject::setBasePosition(position);
1227 m_position_not_sent = true;
1230 void PlayerSAO::setPos(v3f pos)
1234 m_player->setPosition(pos);
1235 // Movement caused by this command is always valid
1236 m_last_good_position = pos;
1237 // Force position change on client
1241 void PlayerSAO::moveTo(v3f pos, bool continuous)
1245 m_player->setPosition(pos);
1246 // Movement caused by this command is always valid
1247 m_last_good_position = pos;
1248 // Force position change on client
1252 void PlayerSAO::setYaw(float yaw)
1254 m_player->setYaw(yaw);
1255 // Force change on client
1259 void PlayerSAO::setPitch(float pitch)
1261 m_player->setPitch(pitch);
1262 // Force change on client
1266 int PlayerSAO::punch(v3f dir,
1267 const ToolCapabilities *toolcap,
1268 ServerActiveObject *puncher,
1269 float time_from_last_punch)
1271 // It's best that attachments cannot be punched
1278 // No effect if PvP disabled
1279 if(g_settings->getBool("enable_pvp") == false){
1280 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1281 std::string str = gob_cmd_punched(0, getHP());
1282 // create message and add to list
1283 ActiveObjectMessage aom(getId(), true, str);
1284 m_messages_out.push_back(aom);
1289 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1290 time_from_last_punch);
1292 std::string punchername = "nil";
1295 punchername = puncher->getDescription();
1297 actionstream<<"Player "<<m_player->getName()<<" punched by "
1298 <<punchername<<", damage "<<hitparams.hp
1301 setHP(getHP() - hitparams.hp);
1303 return hitparams.wear;
1306 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1310 s16 PlayerSAO::getHP() const
1312 return m_player->hp;
1315 s16 PlayerSAO::readDamage()
1317 s16 damage = m_damage;
1322 void PlayerSAO::setHP(s16 hp)
1324 s16 oldhp = m_player->hp;
1328 else if(hp > PLAYER_MAX_HP)
1331 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1333 m_hp_not_sent = true; // fix wrong prediction on client
1340 m_hp_not_sent = true;
1342 m_damage += oldhp - hp;
1345 // Update properties on death
1346 if((hp == 0) != (oldhp == 0))
1347 m_properties_sent = false;
1350 u16 PlayerSAO::getBreath() const
1352 return m_player->getBreath();
1355 void PlayerSAO::setBreath(u16 breath)
1357 m_player->setBreath(breath);
1360 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1362 m_armor_groups = armor_groups;
1363 m_armor_groups_sent = false;
1366 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1368 // store these so they can be updated to clients
1369 m_animation_range = frame_range;
1370 m_animation_speed = frame_speed;
1371 m_animation_blend = frame_blend;
1372 m_animation_sent = false;
1375 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1377 // store these so they can be updated to clients
1378 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1379 m_bone_position_sent = false;
1382 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1384 // Attachments need to be handled on both the server and client.
1385 // If we just attach on the server, we can only copy the position of the parent. Attachments
1386 // are still sent to clients at an interval so players might see them lagging, plus we can't
1387 // read and attach to skeletal bones.
1388 // If we just attach on the client, the server still sees the child at its original location.
1389 // This breaks some things so we also give the server the most accurate representation
1390 // even if players only see the client changes.
1392 m_attachment_parent_id = parent_id;
1393 m_attachment_bone = bone;
1394 m_attachment_position = position;
1395 m_attachment_rotation = rotation;
1396 m_attachment_sent = false;
1399 ObjectProperties* PlayerSAO::accessObjectProperties()
1404 void PlayerSAO::notifyObjectPropertiesModified()
1406 m_properties_sent = false;
1409 Inventory* PlayerSAO::getInventory()
1413 const Inventory* PlayerSAO::getInventory() const
1418 InventoryLocation PlayerSAO::getInventoryLocation() const
1420 InventoryLocation loc;
1421 loc.setPlayer(m_player->getName());
1425 void PlayerSAO::setInventoryModified()
1427 m_inventory_not_sent = true;
1430 std::string PlayerSAO::getWieldList() const
1435 int PlayerSAO::getWieldIndex() const
1437 return m_wield_index;
1440 void PlayerSAO::setWieldIndex(int i)
1442 if(i != m_wield_index)
1445 m_wielded_item_not_sent = true;
1449 void PlayerSAO::disconnected()
1453 if(m_player->getPlayerSAO() == this)
1455 m_player->setPlayerSAO(NULL);
1456 m_player->peer_id = 0;
1460 std::string PlayerSAO::getPropertyPacket()
1462 m_prop.is_visible = (true);
1463 return gob_cmd_set_properties(m_prop);
1466 bool PlayerSAO::checkMovementCheat()
1468 bool cheated = false;
1469 if(isAttached() || m_is_singleplayer ||
1470 g_settings->getBool("disable_anticheat"))
1472 m_last_good_position = m_player->getPosition();
1477 Check player movements
1479 NOTE: Actually the server should handle player physics like the
1480 client does and compare player's position to what is calculated
1481 on our side. This is required when eg. players fly due to an
1482 explosion. Altough a node-based alternative might be possible
1483 too, and much more lightweight.
1486 float player_max_speed = 0;
1487 float player_max_speed_up = 0;
1488 if(m_privs.count("fast") != 0){
1490 player_max_speed = m_player->movement_speed_fast;
1491 player_max_speed_up = m_player->movement_speed_fast;
1494 player_max_speed = m_player->movement_speed_walk;
1495 player_max_speed_up = m_player->movement_speed_walk;
1497 // Tolerance. With the lag pool we shouldn't need it.
1498 //player_max_speed *= 2.5;
1499 //player_max_speed_up *= 2.5;
1501 v3f diff = (m_player->getPosition() - m_last_good_position);
1502 float d_vert = diff.Y;
1504 float d_horiz = diff.getLength();
1505 float required_time = d_horiz/player_max_speed;
1506 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1507 required_time = d_vert/player_max_speed;
1508 if(m_move_pool.grab(required_time)){
1509 m_last_good_position = m_player->getPosition();
1511 actionstream<<"Player "<<m_player->getName()
1512 <<" moved too fast; resetting position"
1514 m_player->setPosition(m_last_good_position);
1522 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1523 //update collision box
1524 *toset = m_player->getCollisionbox();
1526 toset->MinEdge += m_base_position;
1527 toset->MaxEdge += m_base_position;
1532 bool PlayerSAO::collideWithObjects(){