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 "util/serialize.h"
22 #include "util/mathconstants.h"
23 #include "collision.h"
24 #include "environment.h"
26 #include "main.h" // For g_profiler
28 #include "serialization.h" // For compressZlib
29 #include "tool.h" // For ToolCapabilities
32 #include "scripting_game.h"
33 #include "genericobject.h"
36 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
42 class DummyLoadSAO : public ServerActiveObject
45 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
46 ServerActiveObject(env, pos)
48 ServerActiveObject::registerType(type, create);
50 // Pretend to be the test object (to fool the client)
52 { return ACTIVEOBJECT_TYPE_TEST; }
53 // And never save to disk
54 bool isStaticAllowed() const
57 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
58 const std::string &data)
60 return new DummyLoadSAO(env, pos, 0);
63 void step(float dtime, bool send_recommended)
66 infostream<<"DummyLoadSAO step"<<std::endl;
69 bool getCollisionBox(aabb3f *toset) {
73 bool collideWithObjects() {
80 // Prototype (registers item for deserialization)
81 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
82 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
83 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
84 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
90 class TestSAO : public ServerActiveObject
93 TestSAO(ServerEnvironment *env, v3f pos):
94 ServerActiveObject(env, pos),
98 ServerActiveObject::registerType(getType(), create);
101 { return ACTIVEOBJECT_TYPE_TEST; }
103 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
104 const std::string &data)
106 return new TestSAO(env, pos);
109 void step(float dtime, bool send_recommended)
118 m_base_position.Y += dtime * BS * 2;
119 if(m_base_position.Y > 8*BS)
120 m_base_position.Y = 2*BS;
122 if(send_recommended == false)
132 data += itos(0); // 0 = position
134 data += itos(m_base_position.X);
136 data += itos(m_base_position.Y);
138 data += itos(m_base_position.Z);
140 ActiveObjectMessage aom(getId(), false, data);
141 m_messages_out.push_back(aom);
145 bool getCollisionBox(aabb3f *toset) {
149 bool collideWithObjects() {
158 // Prototype (registers item for deserialization)
159 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
164 DEPRECATED: New dropped items are implemented in Lua; see
165 builtin/item_entity.lua.
168 class ItemSAO : public ServerActiveObject
172 { return ACTIVEOBJECT_TYPE_ITEM; }
174 float getMinimumSavedMovement()
177 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
178 const std::string &data)
180 std::istringstream is(data, std::ios::binary);
185 // check if version is supported
188 std::string itemstring = deSerializeString(is);
189 infostream<<"create(): Creating item \""
190 <<itemstring<<"\""<<std::endl;
191 return new ItemSAO(env, pos, itemstring);
194 ItemSAO(ServerEnvironment *env, v3f pos,
195 const std::string &itemstring):
196 ServerActiveObject(env, pos),
197 m_itemstring(itemstring),
198 m_itemstring_changed(false),
200 m_last_sent_position(0,0,0)
202 ServerActiveObject::registerType(getType(), create);
205 void step(float dtime, bool send_recommended)
207 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
211 const float interval = 0.2;
212 if(m_move_interval.step(dtime, interval)==false)
216 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
217 collisionMoveResult moveresult;
219 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
220 // Maximum movement without glitches
221 f32 pos_max_d = BS*0.25;
223 if(m_speed_f.getLength()*dtime > pos_max_d)
224 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
225 v3f pos_f = getBasePosition();
226 v3f pos_f_old = pos_f;
227 v3f accel_f = v3f(0,0,0);
229 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
230 pos_max_d, box, stepheight, dtime,
231 pos_f, m_speed_f, accel_f);
233 if(send_recommended == false)
236 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
238 setBasePosition(pos_f);
239 m_last_sent_position = pos_f;
241 std::ostringstream os(std::ios::binary);
242 // command (0 = update position)
245 writeV3F1000(os, m_base_position);
246 // create message and add to list
247 ActiveObjectMessage aom(getId(), false, os.str());
248 m_messages_out.push_back(aom);
250 if(m_itemstring_changed)
252 m_itemstring_changed = false;
254 std::ostringstream os(std::ios::binary);
255 // command (1 = update itemstring)
258 os<<serializeString(m_itemstring);
259 // create message and add to list
260 ActiveObjectMessage aom(getId(), false, os.str());
261 m_messages_out.push_back(aom);
265 std::string getClientInitializationData(u16 protocol_version)
267 std::ostringstream os(std::ios::binary);
271 writeV3F1000(os, m_base_position);
273 os<<serializeString(m_itemstring);
277 std::string getStaticData()
279 infostream<<__FUNCTION_NAME<<std::endl;
280 std::ostringstream os(std::ios::binary);
284 os<<serializeString(m_itemstring);
288 ItemStack createItemStack()
291 IItemDefManager *idef = m_env->getGameDef()->idef();
293 item.deSerialize(m_itemstring, idef);
294 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
295 <<"\" -> item=\""<<item.getItemString()<<"\""
299 catch(SerializationError &e)
301 infostream<<__FUNCTION_NAME<<": serialization error: "
302 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
308 const ToolCapabilities *toolcap,
309 ServerActiveObject *puncher,
310 float time_from_last_punch)
312 // Take item into inventory
313 ItemStack item = createItemStack();
314 Inventory *inv = puncher->getInventory();
317 std::string wieldlist = puncher->getWieldList();
318 ItemStack leftover = inv->addItem(wieldlist, item);
319 puncher->setInventoryModified();
326 m_itemstring = leftover.getItemString();
327 m_itemstring_changed = true;
334 bool getCollisionBox(aabb3f *toset) {
338 bool collideWithObjects() {
343 std::string m_itemstring;
344 bool m_itemstring_changed;
346 v3f m_last_sent_position;
347 IntervalLimiter m_move_interval;
350 // Prototype (registers item for deserialization)
351 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
353 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
354 const std::string &itemstring)
356 return new ItemSAO(env, pos, itemstring);
363 // Prototype (registers item for deserialization)
364 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
366 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
367 const std::string &name, const std::string &state):
368 ServerActiveObject(env, pos),
374 m_acceleration(0,0,0),
376 m_properties_sent(true),
378 m_last_sent_position(0,0,0),
379 m_last_sent_velocity(0,0,0),
380 m_last_sent_position_timer(0),
381 m_last_sent_move_precision(0),
382 m_armor_groups_sent(false),
383 m_animation_speed(0),
384 m_animation_blend(0),
385 m_animation_sent(false),
386 m_bone_position_sent(false),
387 m_attachment_parent_id(0),
388 m_attachment_sent(false)
390 // Only register type if no environment supplied
392 ServerActiveObject::registerType(getType(), create);
396 // Initialize something to armor groups
397 m_armor_groups["fleshy"] = 100;
400 LuaEntitySAO::~LuaEntitySAO()
403 m_env->getScriptIface()->luaentity_Remove(m_id);
407 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
409 ServerActiveObject::addedToEnvironment(dtime_s);
411 // Create entity from name
412 m_registered = m_env->getScriptIface()->
413 luaentity_Add(m_id, m_init_name.c_str());
417 m_env->getScriptIface()->
418 luaentity_GetProperties(m_id, &m_prop);
419 // Initialize HP from properties
420 m_hp = m_prop.hp_max;
421 // Activate entity, supplying serialized state
422 m_env->getScriptIface()->
423 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
427 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
428 const std::string &data)
436 std::istringstream is(data, std::ios::binary);
438 u8 version = readU8(is);
439 // check if version is supported
441 name = deSerializeString(is);
442 state = deSerializeLongString(is);
444 else if(version == 1){
445 name = deSerializeString(is);
446 state = deSerializeLongString(is);
448 velocity = readV3F1000(is);
453 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
454 <<state<<"\")"<<std::endl;
455 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
457 sao->m_velocity = velocity;
462 bool LuaEntitySAO::isAttached()
464 if(!m_attachment_parent_id)
466 // Check if the parent still exists
467 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
473 void LuaEntitySAO::step(float dtime, bool send_recommended)
475 if(!m_properties_sent)
477 m_properties_sent = true;
478 std::string str = getPropertyPacket();
479 // create message and add to list
480 ActiveObjectMessage aom(getId(), true, str);
481 m_messages_out.push_back(aom);
484 // If attached, check that our parent is still there. If it isn't, detach.
485 if(m_attachment_parent_id && !isAttached())
487 m_attachment_parent_id = 0;
488 m_attachment_bone = "";
489 m_attachment_position = v3f(0,0,0);
490 m_attachment_rotation = v3f(0,0,0);
491 sendPosition(false, true);
494 m_last_sent_position_timer += dtime;
496 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
497 // If the object gets detached this comes into effect automatically from the last known origin
500 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
501 m_base_position = pos;
502 m_velocity = v3f(0,0,0);
503 m_acceleration = v3f(0,0,0);
508 core::aabbox3d<f32> box = m_prop.collisionbox;
511 collisionMoveResult moveresult;
512 f32 pos_max_d = BS*0.25; // Distance per iteration
513 v3f p_pos = m_base_position;
514 v3f p_velocity = m_velocity;
515 v3f p_acceleration = m_acceleration;
516 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
517 pos_max_d, box, m_prop.stepheight, dtime,
518 p_pos, p_velocity, p_acceleration,
519 this, m_prop.collideWithObjects);
522 m_base_position = p_pos;
523 m_velocity = p_velocity;
524 m_acceleration = p_acceleration;
526 m_base_position += dtime * m_velocity + 0.5 * dtime
527 * dtime * m_acceleration;
528 m_velocity += dtime * m_acceleration;
531 if((m_prop.automatic_face_movement_dir) &&
532 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
533 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
538 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
541 if(send_recommended == false)
546 // TODO: force send when acceleration changes enough?
547 float minchange = 0.2*BS;
548 if(m_last_sent_position_timer > 1.0){
550 } else if(m_last_sent_position_timer > 0.2){
553 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
554 move_d += m_last_sent_move_precision;
555 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
556 if(move_d > minchange || vel_d > minchange ||
557 fabs(m_yaw - m_last_sent_yaw) > 1.0){
558 sendPosition(true, false);
562 if(m_armor_groups_sent == false){
563 m_armor_groups_sent = true;
564 std::string str = gob_cmd_update_armor_groups(
566 // create message and add to list
567 ActiveObjectMessage aom(getId(), true, str);
568 m_messages_out.push_back(aom);
571 if(m_animation_sent == false){
572 m_animation_sent = true;
573 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
574 // create message and add to list
575 ActiveObjectMessage aom(getId(), true, str);
576 m_messages_out.push_back(aom);
579 if(m_bone_position_sent == false){
580 m_bone_position_sent = true;
581 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
582 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
583 // create message and add to list
584 ActiveObjectMessage aom(getId(), true, str);
585 m_messages_out.push_back(aom);
589 if(m_attachment_sent == false){
590 m_attachment_sent = true;
591 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
592 // create message and add to list
593 ActiveObjectMessage aom(getId(), true, str);
594 m_messages_out.push_back(aom);
598 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
600 std::ostringstream os(std::ios::binary);
602 if(protocol_version >= 14)
604 writeU8(os, 1); // version
605 os<<serializeString(""); // name
606 writeU8(os, 0); // is_player
607 writeS16(os, getId()); //id
608 writeV3F1000(os, m_base_position);
609 writeF1000(os, m_yaw);
612 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
613 os<<serializeLongString(getPropertyPacket()); // message 1
614 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
615 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
616 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
617 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
619 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
623 writeU8(os, 0); // version
624 os<<serializeString(""); // name
625 writeU8(os, 0); // is_player
626 writeV3F1000(os, m_base_position);
627 writeF1000(os, m_yaw);
629 writeU8(os, 2); // number of messages stuffed in here
630 os<<serializeLongString(getPropertyPacket()); // message 1
631 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
638 std::string LuaEntitySAO::getStaticData()
640 verbosestream<<__FUNCTION_NAME<<std::endl;
641 std::ostringstream os(std::ios::binary);
645 os<<serializeString(m_init_name);
648 std::string state = m_env->getScriptIface()->
649 luaentity_GetStaticdata(m_id);
650 os<<serializeLongString(state);
652 os<<serializeLongString(m_init_state);
657 writeV3F1000(os, m_velocity);
659 writeF1000(os, m_yaw);
663 int LuaEntitySAO::punch(v3f dir,
664 const ToolCapabilities *toolcap,
665 ServerActiveObject *puncher,
666 float time_from_last_punch)
669 // Delete unknown LuaEntities when punched
674 // It's best that attachments cannot be punched
678 ItemStack *punchitem = NULL;
679 ItemStack punchitem_static;
681 punchitem_static = puncher->getWieldedItem();
682 punchitem = &punchitem_static;
685 PunchDamageResult result = getPunchDamage(
689 time_from_last_punch);
693 setHP(getHP() - result.damage);
696 std::string punchername = "nil";
699 punchername = puncher->getDescription();
701 actionstream<<getDescription()<<" punched by "
702 <<punchername<<", damage "<<result.damage
703 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
706 std::string str = gob_cmd_punched(result.damage, getHP());
707 // create message and add to list
708 ActiveObjectMessage aom(getId(), true, str);
709 m_messages_out.push_back(aom);
716 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
717 time_from_last_punch, toolcap, dir);
722 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
726 // It's best that attachments cannot be clicked
729 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
732 void LuaEntitySAO::setPos(v3f pos)
736 m_base_position = pos;
737 sendPosition(false, true);
740 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
744 m_base_position = pos;
746 sendPosition(true, true);
749 float LuaEntitySAO::getMinimumSavedMovement()
754 std::string LuaEntitySAO::getDescription()
756 std::ostringstream os(std::ios::binary);
757 os<<"LuaEntitySAO at (";
758 os<<(m_base_position.X/BS)<<",";
759 os<<(m_base_position.Y/BS)<<",";
760 os<<(m_base_position.Z/BS);
765 void LuaEntitySAO::setHP(s16 hp)
771 s16 LuaEntitySAO::getHP() const
776 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
778 m_armor_groups = armor_groups;
779 m_armor_groups_sent = false;
782 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
784 m_animation_range = frame_range;
785 m_animation_speed = frame_speed;
786 m_animation_blend = frame_blend;
787 m_animation_sent = false;
790 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
792 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
793 m_bone_position_sent = false;
796 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
798 // Attachments need to be handled on both the server and client.
799 // If we just attach on the server, we can only copy the position of the parent. Attachments
800 // are still sent to clients at an interval so players might see them lagging, plus we can't
801 // read and attach to skeletal bones.
802 // If we just attach on the client, the server still sees the child at its original location.
803 // This breaks some things so we also give the server the most accurate representation
804 // even if players only see the client changes.
806 m_attachment_parent_id = parent_id;
807 m_attachment_bone = bone;
808 m_attachment_position = position;
809 m_attachment_rotation = rotation;
810 m_attachment_sent = false;
813 ObjectProperties* LuaEntitySAO::accessObjectProperties()
818 void LuaEntitySAO::notifyObjectPropertiesModified()
820 m_properties_sent = false;
823 void LuaEntitySAO::setVelocity(v3f velocity)
825 m_velocity = velocity;
828 v3f LuaEntitySAO::getVelocity()
833 void LuaEntitySAO::setAcceleration(v3f acceleration)
835 m_acceleration = acceleration;
838 v3f LuaEntitySAO::getAcceleration()
840 return m_acceleration;
843 void LuaEntitySAO::setYaw(float yaw)
848 float LuaEntitySAO::getYaw()
853 void LuaEntitySAO::setTextureMod(const std::string &mod)
855 std::string str = gob_cmd_set_texture_mod(mod);
856 // create message and add to list
857 ActiveObjectMessage aom(getId(), true, str);
858 m_messages_out.push_back(aom);
861 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
862 bool select_horiz_by_yawpitch)
864 std::string str = gob_cmd_set_sprite(
868 select_horiz_by_yawpitch
870 // create message and add to list
871 ActiveObjectMessage aom(getId(), true, str);
872 m_messages_out.push_back(aom);
875 std::string LuaEntitySAO::getName()
880 std::string LuaEntitySAO::getPropertyPacket()
882 return gob_cmd_set_properties(m_prop);
885 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
887 // If the object is attached client-side, don't waste bandwidth sending its position to clients
891 m_last_sent_move_precision = m_base_position.getDistanceFrom(
892 m_last_sent_position);
893 m_last_sent_position_timer = 0;
894 m_last_sent_yaw = m_yaw;
895 m_last_sent_position = m_base_position;
896 m_last_sent_velocity = m_velocity;
897 //m_last_sent_acceleration = m_acceleration;
899 float update_interval = m_env->getSendRecommendedInterval();
901 std::string str = gob_cmd_update_position(
910 // create message and add to list
911 ActiveObjectMessage aom(getId(), false, str);
912 m_messages_out.push_back(aom);
915 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
918 //update collision box
919 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
920 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
922 toset->MinEdge += m_base_position;
923 toset->MaxEdge += m_base_position;
931 bool LuaEntitySAO::collideWithObjects(){
932 return m_prop.collideWithObjects;
939 // No prototype, PlayerSAO does not need to be deserialized
941 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
942 const std::set<std::string> &privs, bool is_singleplayer):
943 ServerActiveObject(env_, v3f(0,0,0)),
948 m_last_good_position(0,0,0),
949 m_time_from_last_punch(0),
950 m_nocheat_dig_pos(32767, 32767, 32767),
951 m_nocheat_dig_time(0),
953 m_position_not_sent(false),
954 m_armor_groups_sent(false),
955 m_properties_sent(true),
957 m_is_singleplayer(is_singleplayer),
958 m_animation_speed(0),
959 m_animation_blend(0),
960 m_animation_sent(false),
961 m_bone_position_sent(false),
962 m_attachment_parent_id(0),
963 m_attachment_sent(false),
966 m_inventory_not_sent(false),
967 m_hp_not_sent(false),
968 m_breath_not_sent(false),
969 m_wielded_item_not_sent(false),
970 m_physics_override_speed(1),
971 m_physics_override_jump(1),
972 m_physics_override_gravity(1),
973 m_physics_override_sneak(true),
974 m_physics_override_sneak_glitch(true),
975 m_physics_override_sent(false)
978 assert(m_peer_id != 0);
979 setBasePosition(m_player->getPosition());
980 m_inventory = &m_player->inventory;
981 m_armor_groups["fleshy"] = 100;
983 m_prop.hp_max = PLAYER_MAX_HP;
984 m_prop.physical = false;
986 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
987 // start of default appearance, this should be overwritten by LUA
988 m_prop.visual = "upright_sprite";
989 m_prop.visual_size = v2f(1, 2);
990 m_prop.textures.clear();
991 m_prop.textures.push_back("player.png");
992 m_prop.textures.push_back("player_back.png");
993 m_prop.colors.clear();
994 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
995 m_prop.spritediv = v2s16(1,1);
996 // end of default appearance
997 m_prop.is_visible = true;
998 m_prop.makes_footstep_sound = true;
1001 PlayerSAO::~PlayerSAO()
1003 if(m_inventory != &m_player->inventory)
1008 std::string PlayerSAO::getDescription()
1010 return std::string("player ") + m_player->getName();
1013 // Called after id has been set and has been inserted in environment
1014 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1016 ServerActiveObject::addedToEnvironment(dtime_s);
1017 ServerActiveObject::setBasePosition(m_player->getPosition());
1018 m_player->setPlayerSAO(this);
1019 m_player->peer_id = m_peer_id;
1020 m_last_good_position = m_player->getPosition();
1023 // Called before removing from environment
1024 void PlayerSAO::removingFromEnvironment()
1026 ServerActiveObject::removingFromEnvironment();
1027 if(m_player->getPlayerSAO() == this)
1029 m_player->setPlayerSAO(NULL);
1030 m_player->peer_id = 0;
1031 m_env->savePlayer(m_player->getName());
1032 m_env->removePlayer(m_player->getName());
1036 bool PlayerSAO::isStaticAllowed() const
1041 bool PlayerSAO::unlimitedTransferDistance() const
1043 return g_settings->getBool("unlimited_player_transfer_distance");
1046 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1048 std::ostringstream os(std::ios::binary);
1050 if(protocol_version >= 15)
1052 writeU8(os, 1); // version
1053 os<<serializeString(m_player->getName()); // name
1054 writeU8(os, 1); // is_player
1055 writeS16(os, getId()); //id
1056 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1057 writeF1000(os, m_player->getYaw());
1058 writeS16(os, getHP());
1060 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1061 os<<serializeLongString(getPropertyPacket()); // message 1
1062 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1063 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1064 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1065 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1067 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1068 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
1069 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
1070 m_physics_override_sneak_glitch)); // 5
1074 writeU8(os, 0); // version
1075 os<<serializeString(m_player->getName()); // name
1076 writeU8(os, 1); // is_player
1077 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1078 writeF1000(os, m_player->getYaw());
1079 writeS16(os, getHP());
1080 writeU8(os, 2); // number of messages stuffed in here
1081 os<<serializeLongString(getPropertyPacket()); // message 1
1082 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1089 std::string PlayerSAO::getStaticData()
1095 bool PlayerSAO::isAttached()
1097 if(!m_attachment_parent_id)
1099 // Check if the parent still exists
1100 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1106 void PlayerSAO::step(float dtime, bool send_recommended)
1108 if(!m_properties_sent)
1110 m_properties_sent = true;
1111 std::string str = getPropertyPacket();
1112 // create message and add to list
1113 ActiveObjectMessage aom(getId(), true, str);
1114 m_messages_out.push_back(aom);
1117 // If attached, check that our parent is still there. If it isn't, detach.
1118 if(m_attachment_parent_id && !isAttached())
1120 m_attachment_parent_id = 0;
1121 m_attachment_bone = "";
1122 m_attachment_position = v3f(0,0,0);
1123 m_attachment_rotation = v3f(0,0,0);
1124 m_player->setPosition(m_last_good_position);
1128 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1130 // Set lag pool maximums based on estimated lag
1131 const float LAG_POOL_MIN = 5.0;
1132 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1133 if(lag_pool_max < LAG_POOL_MIN)
1134 lag_pool_max = LAG_POOL_MIN;
1135 m_dig_pool.setMax(lag_pool_max);
1136 m_move_pool.setMax(lag_pool_max);
1138 // Increment cheat prevention timers
1139 m_dig_pool.add(dtime);
1140 m_move_pool.add(dtime);
1141 m_time_from_last_punch += dtime;
1142 m_nocheat_dig_time += dtime;
1144 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1145 // If the object gets detached this comes into effect automatically from the last known origin
1148 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1149 m_last_good_position = pos;
1150 m_player->setPosition(pos);
1153 if(send_recommended == false)
1156 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1157 if(m_position_not_sent && !isAttached())
1159 m_position_not_sent = false;
1160 float update_interval = m_env->getSendRecommendedInterval();
1162 if(isAttached()) // Just in case we ever do send attachment position too
1163 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1165 pos = m_player->getPosition() + v3f(0,BS*1,0);
1166 std::string str = gob_cmd_update_position(
1175 // create message and add to list
1176 ActiveObjectMessage aom(getId(), false, str);
1177 m_messages_out.push_back(aom);
1180 if(m_wielded_item_not_sent)
1182 m_wielded_item_not_sent = false;
1183 // GenericCAO has no special way to show this
1186 if(m_armor_groups_sent == false){
1187 m_armor_groups_sent = true;
1188 std::string str = gob_cmd_update_armor_groups(
1190 // create message and add to list
1191 ActiveObjectMessage aom(getId(), true, str);
1192 m_messages_out.push_back(aom);
1195 if(m_physics_override_sent == false){
1196 m_physics_override_sent = true;
1197 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1198 m_physics_override_jump, m_physics_override_gravity,
1199 m_physics_override_sneak, m_physics_override_sneak_glitch);
1200 // create message and add to list
1201 ActiveObjectMessage aom(getId(), true, str);
1202 m_messages_out.push_back(aom);
1205 if(m_animation_sent == false){
1206 m_animation_sent = true;
1207 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1208 // create message and add to list
1209 ActiveObjectMessage aom(getId(), true, str);
1210 m_messages_out.push_back(aom);
1213 if(m_bone_position_sent == false){
1214 m_bone_position_sent = true;
1215 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1216 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1217 // create message and add to list
1218 ActiveObjectMessage aom(getId(), true, str);
1219 m_messages_out.push_back(aom);
1223 if(m_attachment_sent == false){
1224 m_attachment_sent = true;
1225 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1226 // create message and add to list
1227 ActiveObjectMessage aom(getId(), true, str);
1228 m_messages_out.push_back(aom);
1232 void PlayerSAO::setBasePosition(const v3f &position)
1234 // This needs to be ran for attachments too
1235 ServerActiveObject::setBasePosition(position);
1236 m_position_not_sent = true;
1239 void PlayerSAO::setPos(v3f pos)
1243 m_player->setPosition(pos);
1244 // Movement caused by this command is always valid
1245 m_last_good_position = pos;
1246 // Force position change on client
1250 void PlayerSAO::moveTo(v3f pos, bool continuous)
1254 m_player->setPosition(pos);
1255 // Movement caused by this command is always valid
1256 m_last_good_position = pos;
1257 // Force position change on client
1261 void PlayerSAO::setYaw(float yaw)
1263 m_player->setYaw(yaw);
1264 // Force change on client
1268 void PlayerSAO::setPitch(float pitch)
1270 m_player->setPitch(pitch);
1271 // Force change on client
1275 int PlayerSAO::punch(v3f dir,
1276 const ToolCapabilities *toolcap,
1277 ServerActiveObject *puncher,
1278 float time_from_last_punch)
1280 // It's best that attachments cannot be punched
1287 // No effect if PvP disabled
1288 if(g_settings->getBool("enable_pvp") == false){
1289 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1290 std::string str = gob_cmd_punched(0, getHP());
1291 // create message and add to list
1292 ActiveObjectMessage aom(getId(), true, str);
1293 m_messages_out.push_back(aom);
1298 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1299 time_from_last_punch);
1301 std::string punchername = "nil";
1304 punchername = puncher->getDescription();
1306 actionstream<<"Player "<<m_player->getName()<<" punched by "
1307 <<punchername<<", damage "<<hitparams.hp
1310 setHP(getHP() - hitparams.hp);
1312 return hitparams.wear;
1315 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1319 s16 PlayerSAO::getHP() const
1321 return m_player->hp;
1324 s16 PlayerSAO::readDamage()
1326 s16 damage = m_damage;
1331 void PlayerSAO::setHP(s16 hp)
1333 s16 oldhp = m_player->hp;
1337 else if(hp > PLAYER_MAX_HP)
1340 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1342 m_hp_not_sent = true; // fix wrong prediction on client
1349 m_hp_not_sent = true;
1351 m_damage += oldhp - hp;
1354 // Update properties on death
1355 if((hp == 0) != (oldhp == 0))
1356 m_properties_sent = false;
1359 u16 PlayerSAO::getBreath() const
1361 return m_player->getBreath();
1364 void PlayerSAO::setBreath(u16 breath)
1366 m_player->setBreath(breath);
1369 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1371 m_armor_groups = armor_groups;
1372 m_armor_groups_sent = false;
1375 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1377 // store these so they can be updated to clients
1378 m_animation_range = frame_range;
1379 m_animation_speed = frame_speed;
1380 m_animation_blend = frame_blend;
1381 m_animation_sent = false;
1384 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1386 // store these so they can be updated to clients
1387 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1388 m_bone_position_sent = false;
1391 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1393 // Attachments need to be handled on both the server and client.
1394 // If we just attach on the server, we can only copy the position of the parent. Attachments
1395 // are still sent to clients at an interval so players might see them lagging, plus we can't
1396 // read and attach to skeletal bones.
1397 // If we just attach on the client, the server still sees the child at its original location.
1398 // This breaks some things so we also give the server the most accurate representation
1399 // even if players only see the client changes.
1401 m_attachment_parent_id = parent_id;
1402 m_attachment_bone = bone;
1403 m_attachment_position = position;
1404 m_attachment_rotation = rotation;
1405 m_attachment_sent = false;
1408 ObjectProperties* PlayerSAO::accessObjectProperties()
1413 void PlayerSAO::notifyObjectPropertiesModified()
1415 m_properties_sent = false;
1418 Inventory* PlayerSAO::getInventory()
1422 const Inventory* PlayerSAO::getInventory() const
1427 InventoryLocation PlayerSAO::getInventoryLocation() const
1429 InventoryLocation loc;
1430 loc.setPlayer(m_player->getName());
1434 void PlayerSAO::setInventoryModified()
1436 m_inventory_not_sent = true;
1439 std::string PlayerSAO::getWieldList() const
1444 int PlayerSAO::getWieldIndex() const
1446 return m_wield_index;
1449 void PlayerSAO::setWieldIndex(int i)
1451 if(i != m_wield_index)
1454 m_wielded_item_not_sent = true;
1458 void PlayerSAO::disconnected()
1462 if(m_player->getPlayerSAO() == this)
1464 m_player->setPlayerSAO(NULL);
1465 m_player->peer_id = 0;
1469 std::string PlayerSAO::getPropertyPacket()
1471 m_prop.is_visible = (true);
1472 return gob_cmd_set_properties(m_prop);
1475 bool PlayerSAO::checkMovementCheat()
1477 bool cheated = false;
1478 if(isAttached() || m_is_singleplayer ||
1479 g_settings->getBool("disable_anticheat"))
1481 m_last_good_position = m_player->getPosition();
1486 Check player movements
1488 NOTE: Actually the server should handle player physics like the
1489 client does and compare player's position to what is calculated
1490 on our side. This is required when eg. players fly due to an
1491 explosion. Altough a node-based alternative might be possible
1492 too, and much more lightweight.
1495 float player_max_speed = 0;
1496 float player_max_speed_up = 0;
1497 if(m_privs.count("fast") != 0){
1499 player_max_speed = m_player->movement_speed_fast;
1500 player_max_speed_up = m_player->movement_speed_fast;
1503 player_max_speed = m_player->movement_speed_walk;
1504 player_max_speed_up = m_player->movement_speed_walk;
1506 // Tolerance. With the lag pool we shouldn't need it.
1507 //player_max_speed *= 2.5;
1508 //player_max_speed_up *= 2.5;
1510 v3f diff = (m_player->getPosition() - m_last_good_position);
1511 float d_vert = diff.Y;
1513 float d_horiz = diff.getLength();
1514 float required_time = d_horiz/player_max_speed;
1515 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1516 required_time = d_vert/player_max_speed;
1517 if(m_move_pool.grab(required_time)){
1518 m_last_good_position = m_player->getPosition();
1520 actionstream<<"Player "<<m_player->getName()
1521 <<" moved too fast; resetting position"
1523 m_player->setPosition(m_last_good_position);
1531 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1532 //update collision box
1533 *toset = m_player->getCollisionbox();
1535 toset->MinEdge += m_base_position;
1536 toset->MaxEdge += m_base_position;
1541 bool PlayerSAO::collideWithObjects(){