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;
1030 m_env->savePlayer(m_player->getName());
1031 m_env->removePlayer(m_player->getName());
1035 bool PlayerSAO::isStaticAllowed() const
1040 bool PlayerSAO::unlimitedTransferDistance() const
1042 return g_settings->getBool("unlimited_player_transfer_distance");
1045 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1047 std::ostringstream os(std::ios::binary);
1049 if(protocol_version >= 15)
1051 writeU8(os, 1); // version
1052 os<<serializeString(m_player->getName()); // name
1053 writeU8(os, 1); // is_player
1054 writeS16(os, getId()); //id
1055 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1056 writeF1000(os, m_player->getYaw());
1057 writeS16(os, getHP());
1059 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1060 os<<serializeLongString(getPropertyPacket()); // message 1
1061 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1062 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1063 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1064 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1066 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1067 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
1068 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
1069 m_physics_override_sneak_glitch)); // 5
1073 writeU8(os, 0); // version
1074 os<<serializeString(m_player->getName()); // name
1075 writeU8(os, 1); // is_player
1076 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1077 writeF1000(os, m_player->getYaw());
1078 writeS16(os, getHP());
1079 writeU8(os, 2); // number of messages stuffed in here
1080 os<<serializeLongString(getPropertyPacket()); // message 1
1081 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1088 std::string PlayerSAO::getStaticData()
1094 bool PlayerSAO::isAttached()
1096 if(!m_attachment_parent_id)
1098 // Check if the parent still exists
1099 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1105 void PlayerSAO::step(float dtime, bool send_recommended)
1107 if(!m_properties_sent)
1109 m_properties_sent = true;
1110 std::string str = getPropertyPacket();
1111 // create message and add to list
1112 ActiveObjectMessage aom(getId(), true, str);
1113 m_messages_out.push_back(aom);
1116 // If attached, check that our parent is still there. If it isn't, detach.
1117 if(m_attachment_parent_id && !isAttached())
1119 m_attachment_parent_id = 0;
1120 m_attachment_bone = "";
1121 m_attachment_position = v3f(0,0,0);
1122 m_attachment_rotation = v3f(0,0,0);
1123 m_player->setPosition(m_last_good_position);
1127 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1129 // Set lag pool maximums based on estimated lag
1130 const float LAG_POOL_MIN = 5.0;
1131 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1132 if(lag_pool_max < LAG_POOL_MIN)
1133 lag_pool_max = LAG_POOL_MIN;
1134 m_dig_pool.setMax(lag_pool_max);
1135 m_move_pool.setMax(lag_pool_max);
1137 // Increment cheat prevention timers
1138 m_dig_pool.add(dtime);
1139 m_move_pool.add(dtime);
1140 m_time_from_last_punch += dtime;
1141 m_nocheat_dig_time += dtime;
1143 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1144 // If the object gets detached this comes into effect automatically from the last known origin
1147 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1148 m_last_good_position = pos;
1149 m_player->setPosition(pos);
1152 if(send_recommended == false)
1155 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1156 if(m_position_not_sent && !isAttached())
1158 m_position_not_sent = false;
1159 float update_interval = m_env->getSendRecommendedInterval();
1161 if(isAttached()) // Just in case we ever do send attachment position too
1162 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1164 pos = m_player->getPosition() + v3f(0,BS*1,0);
1165 std::string str = gob_cmd_update_position(
1174 // create message and add to list
1175 ActiveObjectMessage aom(getId(), false, str);
1176 m_messages_out.push_back(aom);
1179 if(m_wielded_item_not_sent)
1181 m_wielded_item_not_sent = false;
1182 // GenericCAO has no special way to show this
1185 if(m_armor_groups_sent == false){
1186 m_armor_groups_sent = true;
1187 std::string str = gob_cmd_update_armor_groups(
1189 // create message and add to list
1190 ActiveObjectMessage aom(getId(), true, str);
1191 m_messages_out.push_back(aom);
1194 if(m_physics_override_sent == false){
1195 m_physics_override_sent = true;
1196 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1197 m_physics_override_jump, m_physics_override_gravity,
1198 m_physics_override_sneak, m_physics_override_sneak_glitch);
1199 // create message and add to list
1200 ActiveObjectMessage aom(getId(), true, str);
1201 m_messages_out.push_back(aom);
1204 if(m_animation_sent == false){
1205 m_animation_sent = true;
1206 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1207 // create message and add to list
1208 ActiveObjectMessage aom(getId(), true, str);
1209 m_messages_out.push_back(aom);
1212 if(m_bone_position_sent == false){
1213 m_bone_position_sent = true;
1214 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1215 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1216 // create message and add to list
1217 ActiveObjectMessage aom(getId(), true, str);
1218 m_messages_out.push_back(aom);
1222 if(m_attachment_sent == false){
1223 m_attachment_sent = true;
1224 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1225 // create message and add to list
1226 ActiveObjectMessage aom(getId(), true, str);
1227 m_messages_out.push_back(aom);
1231 void PlayerSAO::setBasePosition(const v3f &position)
1233 // This needs to be ran for attachments too
1234 ServerActiveObject::setBasePosition(position);
1235 m_position_not_sent = true;
1238 void PlayerSAO::setPos(v3f pos)
1242 m_player->setPosition(pos);
1243 // Movement caused by this command is always valid
1244 m_last_good_position = pos;
1245 // Force position change on client
1249 void PlayerSAO::moveTo(v3f pos, bool continuous)
1253 m_player->setPosition(pos);
1254 // Movement caused by this command is always valid
1255 m_last_good_position = pos;
1256 // Force position change on client
1260 void PlayerSAO::setYaw(float yaw)
1262 m_player->setYaw(yaw);
1263 // Force change on client
1267 void PlayerSAO::setPitch(float pitch)
1269 m_player->setPitch(pitch);
1270 // Force change on client
1274 int PlayerSAO::punch(v3f dir,
1275 const ToolCapabilities *toolcap,
1276 ServerActiveObject *puncher,
1277 float time_from_last_punch)
1279 // It's best that attachments cannot be punched
1286 // No effect if PvP disabled
1287 if(g_settings->getBool("enable_pvp") == false){
1288 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1289 std::string str = gob_cmd_punched(0, getHP());
1290 // create message and add to list
1291 ActiveObjectMessage aom(getId(), true, str);
1292 m_messages_out.push_back(aom);
1297 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1298 time_from_last_punch);
1300 std::string punchername = "nil";
1303 punchername = puncher->getDescription();
1305 actionstream<<"Player "<<m_player->getName()<<" punched by "
1306 <<punchername<<", damage "<<hitparams.hp
1309 setHP(getHP() - hitparams.hp);
1311 return hitparams.wear;
1314 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1318 s16 PlayerSAO::getHP() const
1320 return m_player->hp;
1323 s16 PlayerSAO::readDamage()
1325 s16 damage = m_damage;
1330 void PlayerSAO::setHP(s16 hp)
1332 s16 oldhp = m_player->hp;
1336 else if(hp > PLAYER_MAX_HP)
1339 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1341 m_hp_not_sent = true; // fix wrong prediction on client
1348 m_hp_not_sent = true;
1350 m_damage += oldhp - hp;
1353 // Update properties on death
1354 if((hp == 0) != (oldhp == 0))
1355 m_properties_sent = false;
1358 u16 PlayerSAO::getBreath() const
1360 return m_player->getBreath();
1363 void PlayerSAO::setBreath(u16 breath)
1365 m_player->setBreath(breath);
1368 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1370 m_armor_groups = armor_groups;
1371 m_armor_groups_sent = false;
1374 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1376 // store these so they can be updated to clients
1377 m_animation_range = frame_range;
1378 m_animation_speed = frame_speed;
1379 m_animation_blend = frame_blend;
1380 m_animation_sent = false;
1383 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1385 // store these so they can be updated to clients
1386 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1387 m_bone_position_sent = false;
1390 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1392 // Attachments need to be handled on both the server and client.
1393 // If we just attach on the server, we can only copy the position of the parent. Attachments
1394 // are still sent to clients at an interval so players might see them lagging, plus we can't
1395 // read and attach to skeletal bones.
1396 // If we just attach on the client, the server still sees the child at its original location.
1397 // This breaks some things so we also give the server the most accurate representation
1398 // even if players only see the client changes.
1400 m_attachment_parent_id = parent_id;
1401 m_attachment_bone = bone;
1402 m_attachment_position = position;
1403 m_attachment_rotation = rotation;
1404 m_attachment_sent = false;
1407 ObjectProperties* PlayerSAO::accessObjectProperties()
1412 void PlayerSAO::notifyObjectPropertiesModified()
1414 m_properties_sent = false;
1417 Inventory* PlayerSAO::getInventory()
1421 const Inventory* PlayerSAO::getInventory() const
1426 InventoryLocation PlayerSAO::getInventoryLocation() const
1428 InventoryLocation loc;
1429 loc.setPlayer(m_player->getName());
1433 void PlayerSAO::setInventoryModified()
1435 m_inventory_not_sent = true;
1438 std::string PlayerSAO::getWieldList() const
1443 int PlayerSAO::getWieldIndex() const
1445 return m_wield_index;
1448 void PlayerSAO::setWieldIndex(int i)
1450 if(i != m_wield_index)
1453 m_wielded_item_not_sent = true;
1457 void PlayerSAO::disconnected()
1461 if(m_player->getPlayerSAO() == this)
1463 m_player->setPlayerSAO(NULL);
1464 m_player->peer_id = 0;
1468 std::string PlayerSAO::getPropertyPacket()
1470 m_prop.is_visible = (true);
1471 return gob_cmd_set_properties(m_prop);
1474 bool PlayerSAO::checkMovementCheat()
1476 bool cheated = false;
1477 if(isAttached() || m_is_singleplayer ||
1478 g_settings->getBool("disable_anticheat"))
1480 m_last_good_position = m_player->getPosition();
1485 Check player movements
1487 NOTE: Actually the server should handle player physics like the
1488 client does and compare player's position to what is calculated
1489 on our side. This is required when eg. players fly due to an
1490 explosion. Altough a node-based alternative might be possible
1491 too, and much more lightweight.
1494 float player_max_speed = 0;
1495 float player_max_speed_up = 0;
1496 if(m_privs.count("fast") != 0){
1498 player_max_speed = m_player->movement_speed_fast;
1499 player_max_speed_up = m_player->movement_speed_fast;
1502 player_max_speed = m_player->movement_speed_walk;
1503 player_max_speed_up = m_player->movement_speed_walk;
1505 // Tolerance. With the lag pool we shouldn't need it.
1506 //player_max_speed *= 2.5;
1507 //player_max_speed_up *= 2.5;
1509 v3f diff = (m_player->getPosition() - m_last_good_position);
1510 float d_vert = diff.Y;
1512 float d_horiz = diff.getLength();
1513 float required_time = d_horiz/player_max_speed;
1514 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1515 required_time = d_vert/player_max_speed;
1516 if(m_move_pool.grab(required_time)){
1517 m_last_good_position = m_player->getPosition();
1519 actionstream<<"Player "<<m_player->getName()
1520 <<" moved too fast; resetting position"
1522 m_player->setPosition(m_last_good_position);
1530 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1531 //update collision box
1532 *toset = m_player->getCollisionbox();
1534 toset->MinEdge += m_base_position;
1535 toset->MaxEdge += m_base_position;
1540 bool PlayerSAO::collideWithObjects(){