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_sneak(true),
973 m_physics_override_sneak_glitch(true),
974 m_physics_override_sent(false)
977 assert(m_peer_id != 0);
978 setBasePosition(m_player->getPosition());
979 m_inventory = &m_player->inventory;
980 m_armor_groups["fleshy"] = 100;
982 m_prop.hp_max = PLAYER_MAX_HP;
983 m_prop.physical = false;
985 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
986 // start of default appearance, this should be overwritten by LUA
987 m_prop.visual = "upright_sprite";
988 m_prop.visual_size = v2f(1, 2);
989 m_prop.textures.clear();
990 m_prop.textures.push_back("player.png");
991 m_prop.textures.push_back("player_back.png");
992 m_prop.colors.clear();
993 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
994 m_prop.spritediv = v2s16(1,1);
995 // end of default appearance
996 m_prop.is_visible = true;
997 m_prop.makes_footstep_sound = true;
1000 PlayerSAO::~PlayerSAO()
1002 if(m_inventory != &m_player->inventory)
1007 std::string PlayerSAO::getDescription()
1009 return std::string("player ") + m_player->getName();
1012 // Called after id has been set and has been inserted in environment
1013 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1015 ServerActiveObject::addedToEnvironment(dtime_s);
1016 ServerActiveObject::setBasePosition(m_player->getPosition());
1017 m_player->setPlayerSAO(this);
1018 m_player->peer_id = m_peer_id;
1019 m_last_good_position = m_player->getPosition();
1022 // Called before removing from environment
1023 void PlayerSAO::removingFromEnvironment()
1025 ServerActiveObject::removingFromEnvironment();
1026 if(m_player->getPlayerSAO() == this)
1028 m_player->setPlayerSAO(NULL);
1029 m_player->peer_id = 0;
1033 bool PlayerSAO::isStaticAllowed() const
1038 bool PlayerSAO::unlimitedTransferDistance() const
1040 return g_settings->getBool("unlimited_player_transfer_distance");
1043 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1045 std::ostringstream os(std::ios::binary);
1047 if(protocol_version >= 15)
1049 writeU8(os, 1); // version
1050 os<<serializeString(m_player->getName()); // name
1051 writeU8(os, 1); // is_player
1052 writeS16(os, getId()); //id
1053 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1054 writeF1000(os, m_player->getYaw());
1055 writeS16(os, getHP());
1057 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1058 os<<serializeLongString(getPropertyPacket()); // message 1
1059 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1060 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1061 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1062 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1064 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1065 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
1066 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
1067 m_physics_override_sneak_glitch)); // 5
1071 writeU8(os, 0); // version
1072 os<<serializeString(m_player->getName()); // name
1073 writeU8(os, 1); // is_player
1074 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1075 writeF1000(os, m_player->getYaw());
1076 writeS16(os, getHP());
1077 writeU8(os, 2); // number of messages stuffed in here
1078 os<<serializeLongString(getPropertyPacket()); // message 1
1079 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1086 std::string PlayerSAO::getStaticData()
1092 bool PlayerSAO::isAttached()
1094 if(!m_attachment_parent_id)
1096 // Check if the parent still exists
1097 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1103 void PlayerSAO::step(float dtime, bool send_recommended)
1105 if(!m_properties_sent)
1107 m_properties_sent = true;
1108 std::string str = getPropertyPacket();
1109 // create message and add to list
1110 ActiveObjectMessage aom(getId(), true, str);
1111 m_messages_out.push_back(aom);
1114 // If attached, check that our parent is still there. If it isn't, detach.
1115 if(m_attachment_parent_id && !isAttached())
1117 m_attachment_parent_id = 0;
1118 m_attachment_bone = "";
1119 m_attachment_position = v3f(0,0,0);
1120 m_attachment_rotation = v3f(0,0,0);
1121 m_player->setPosition(m_last_good_position);
1125 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1127 // Set lag pool maximums based on estimated lag
1128 const float LAG_POOL_MIN = 5.0;
1129 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1130 if(lag_pool_max < LAG_POOL_MIN)
1131 lag_pool_max = LAG_POOL_MIN;
1132 m_dig_pool.setMax(lag_pool_max);
1133 m_move_pool.setMax(lag_pool_max);
1135 // Increment cheat prevention timers
1136 m_dig_pool.add(dtime);
1137 m_move_pool.add(dtime);
1138 m_time_from_last_punch += dtime;
1139 m_nocheat_dig_time += dtime;
1141 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1142 // If the object gets detached this comes into effect automatically from the last known origin
1145 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1146 m_last_good_position = pos;
1147 m_player->setPosition(pos);
1150 if(send_recommended == false)
1153 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1154 if(m_position_not_sent && !isAttached())
1156 m_position_not_sent = false;
1157 float update_interval = m_env->getSendRecommendedInterval();
1159 if(isAttached()) // Just in case we ever do send attachment position too
1160 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1162 pos = m_player->getPosition() + v3f(0,BS*1,0);
1163 std::string str = gob_cmd_update_position(
1172 // create message and add to list
1173 ActiveObjectMessage aom(getId(), false, str);
1174 m_messages_out.push_back(aom);
1177 if(m_wielded_item_not_sent)
1179 m_wielded_item_not_sent = false;
1180 // GenericCAO has no special way to show this
1183 if(m_armor_groups_sent == false){
1184 m_armor_groups_sent = true;
1185 std::string str = gob_cmd_update_armor_groups(
1187 // create message and add to list
1188 ActiveObjectMessage aom(getId(), true, str);
1189 m_messages_out.push_back(aom);
1192 if(m_physics_override_sent == false){
1193 m_physics_override_sent = true;
1194 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1195 m_physics_override_jump, m_physics_override_gravity,
1196 m_physics_override_sneak, m_physics_override_sneak_glitch);
1197 // create message and add to list
1198 ActiveObjectMessage aom(getId(), true, str);
1199 m_messages_out.push_back(aom);
1202 if(m_animation_sent == false){
1203 m_animation_sent = true;
1204 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1205 // create message and add to list
1206 ActiveObjectMessage aom(getId(), true, str);
1207 m_messages_out.push_back(aom);
1210 if(m_bone_position_sent == false){
1211 m_bone_position_sent = true;
1212 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1213 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1214 // create message and add to list
1215 ActiveObjectMessage aom(getId(), true, str);
1216 m_messages_out.push_back(aom);
1220 if(m_attachment_sent == false){
1221 m_attachment_sent = true;
1222 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1223 // create message and add to list
1224 ActiveObjectMessage aom(getId(), true, str);
1225 m_messages_out.push_back(aom);
1229 void PlayerSAO::setBasePosition(const v3f &position)
1231 // This needs to be ran for attachments too
1232 ServerActiveObject::setBasePosition(position);
1233 m_position_not_sent = true;
1236 void PlayerSAO::setPos(v3f pos)
1240 m_player->setPosition(pos);
1241 // Movement caused by this command is always valid
1242 m_last_good_position = pos;
1243 // Force position change on client
1247 void PlayerSAO::moveTo(v3f pos, bool continuous)
1251 m_player->setPosition(pos);
1252 // Movement caused by this command is always valid
1253 m_last_good_position = pos;
1254 // Force position change on client
1258 void PlayerSAO::setYaw(float yaw)
1260 m_player->setYaw(yaw);
1261 // Force change on client
1265 void PlayerSAO::setPitch(float pitch)
1267 m_player->setPitch(pitch);
1268 // Force change on client
1272 int PlayerSAO::punch(v3f dir,
1273 const ToolCapabilities *toolcap,
1274 ServerActiveObject *puncher,
1275 float time_from_last_punch)
1277 // It's best that attachments cannot be punched
1284 // No effect if PvP disabled
1285 if(g_settings->getBool("enable_pvp") == false){
1286 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1287 std::string str = gob_cmd_punched(0, getHP());
1288 // create message and add to list
1289 ActiveObjectMessage aom(getId(), true, str);
1290 m_messages_out.push_back(aom);
1295 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1296 time_from_last_punch);
1298 std::string punchername = "nil";
1301 punchername = puncher->getDescription();
1303 actionstream<<"Player "<<m_player->getName()<<" punched by "
1304 <<punchername<<", damage "<<hitparams.hp
1307 setHP(getHP() - hitparams.hp);
1309 return hitparams.wear;
1312 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1316 s16 PlayerSAO::getHP() const
1318 return m_player->hp;
1321 s16 PlayerSAO::readDamage()
1323 s16 damage = m_damage;
1328 void PlayerSAO::setHP(s16 hp)
1330 s16 oldhp = m_player->hp;
1334 else if(hp > PLAYER_MAX_HP)
1337 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1339 m_hp_not_sent = true; // fix wrong prediction on client
1346 m_hp_not_sent = true;
1348 m_damage += oldhp - hp;
1351 // Update properties on death
1352 if((hp == 0) != (oldhp == 0))
1353 m_properties_sent = false;
1356 u16 PlayerSAO::getBreath() const
1358 return m_player->getBreath();
1361 void PlayerSAO::setBreath(u16 breath)
1363 m_player->setBreath(breath);
1366 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1368 m_armor_groups = armor_groups;
1369 m_armor_groups_sent = false;
1372 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1374 // store these so they can be updated to clients
1375 m_animation_range = frame_range;
1376 m_animation_speed = frame_speed;
1377 m_animation_blend = frame_blend;
1378 m_animation_sent = false;
1381 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1383 // store these so they can be updated to clients
1384 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1385 m_bone_position_sent = false;
1388 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1390 // Attachments need to be handled on both the server and client.
1391 // If we just attach on the server, we can only copy the position of the parent. Attachments
1392 // are still sent to clients at an interval so players might see them lagging, plus we can't
1393 // read and attach to skeletal bones.
1394 // If we just attach on the client, the server still sees the child at its original location.
1395 // This breaks some things so we also give the server the most accurate representation
1396 // even if players only see the client changes.
1398 m_attachment_parent_id = parent_id;
1399 m_attachment_bone = bone;
1400 m_attachment_position = position;
1401 m_attachment_rotation = rotation;
1402 m_attachment_sent = false;
1405 ObjectProperties* PlayerSAO::accessObjectProperties()
1410 void PlayerSAO::notifyObjectPropertiesModified()
1412 m_properties_sent = false;
1415 Inventory* PlayerSAO::getInventory()
1419 const Inventory* PlayerSAO::getInventory() const
1424 InventoryLocation PlayerSAO::getInventoryLocation() const
1426 InventoryLocation loc;
1427 loc.setPlayer(m_player->getName());
1431 void PlayerSAO::setInventoryModified()
1433 m_inventory_not_sent = true;
1436 std::string PlayerSAO::getWieldList() const
1441 int PlayerSAO::getWieldIndex() const
1443 return m_wield_index;
1446 void PlayerSAO::setWieldIndex(int i)
1448 if(i != m_wield_index)
1451 m_wielded_item_not_sent = true;
1455 void PlayerSAO::disconnected()
1459 if(m_player->getPlayerSAO() == this)
1461 m_player->setPlayerSAO(NULL);
1462 m_player->peer_id = 0;
1466 std::string PlayerSAO::getPropertyPacket()
1468 m_prop.is_visible = (true);
1469 return gob_cmd_set_properties(m_prop);
1472 bool PlayerSAO::checkMovementCheat()
1474 bool cheated = false;
1475 if(isAttached() || m_is_singleplayer ||
1476 g_settings->getBool("disable_anticheat"))
1478 m_last_good_position = m_player->getPosition();
1483 Check player movements
1485 NOTE: Actually the server should handle player physics like the
1486 client does and compare player's position to what is calculated
1487 on our side. This is required when eg. players fly due to an
1488 explosion. Altough a node-based alternative might be possible
1489 too, and much more lightweight.
1492 float player_max_speed = 0;
1493 float player_max_speed_up = 0;
1494 if(m_privs.count("fast") != 0){
1496 player_max_speed = m_player->movement_speed_fast;
1497 player_max_speed_up = m_player->movement_speed_fast;
1500 player_max_speed = m_player->movement_speed_walk;
1501 player_max_speed_up = m_player->movement_speed_walk;
1503 // Tolerance. With the lag pool we shouldn't need it.
1504 //player_max_speed *= 2.5;
1505 //player_max_speed_up *= 2.5;
1507 v3f diff = (m_player->getPosition() - m_last_good_position);
1508 float d_vert = diff.Y;
1510 float d_horiz = diff.getLength();
1511 float required_time = d_horiz/player_max_speed;
1512 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1513 required_time = d_vert/player_max_speed;
1514 if(m_move_pool.grab(required_time)){
1515 m_last_good_position = m_player->getPosition();
1517 actionstream<<"Player "<<m_player->getName()
1518 <<" moved too fast; resetting position"
1520 m_player->setPosition(m_last_good_position);
1528 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1529 //update collision box
1530 *toset = m_player->getCollisionbox();
1532 toset->MinEdge += m_base_position;
1533 toset->MaxEdge += m_base_position;
1538 bool PlayerSAO::collideWithObjects(){