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 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1043 std::ostringstream os(std::ios::binary);
1045 if(protocol_version >= 15)
1047 writeU8(os, 1); // version
1048 os<<serializeString(m_player->getName()); // name
1049 writeU8(os, 1); // is_player
1050 writeS16(os, getId()); //id
1051 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1052 writeF1000(os, m_player->getYaw());
1053 writeS16(os, getHP());
1055 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1056 os<<serializeLongString(getPropertyPacket()); // message 1
1057 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1058 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1059 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1060 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1062 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1063 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
1064 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
1065 m_physics_override_sneak_glitch)); // 5
1069 writeU8(os, 0); // version
1070 os<<serializeString(m_player->getName()); // name
1071 writeU8(os, 1); // is_player
1072 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1073 writeF1000(os, m_player->getYaw());
1074 writeS16(os, getHP());
1075 writeU8(os, 2); // number of messages stuffed in here
1076 os<<serializeLongString(getPropertyPacket()); // message 1
1077 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1084 std::string PlayerSAO::getStaticData()
1090 bool PlayerSAO::isAttached()
1092 if(!m_attachment_parent_id)
1094 // Check if the parent still exists
1095 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1101 void PlayerSAO::step(float dtime, bool send_recommended)
1103 if(!m_properties_sent)
1105 m_properties_sent = true;
1106 std::string str = getPropertyPacket();
1107 // create message and add to list
1108 ActiveObjectMessage aom(getId(), true, str);
1109 m_messages_out.push_back(aom);
1112 // If attached, check that our parent is still there. If it isn't, detach.
1113 if(m_attachment_parent_id && !isAttached())
1115 m_attachment_parent_id = 0;
1116 m_attachment_bone = "";
1117 m_attachment_position = v3f(0,0,0);
1118 m_attachment_rotation = v3f(0,0,0);
1119 m_player->setPosition(m_last_good_position);
1123 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1125 // Set lag pool maximums based on estimated lag
1126 const float LAG_POOL_MIN = 5.0;
1127 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1128 if(lag_pool_max < LAG_POOL_MIN)
1129 lag_pool_max = LAG_POOL_MIN;
1130 m_dig_pool.setMax(lag_pool_max);
1131 m_move_pool.setMax(lag_pool_max);
1133 // Increment cheat prevention timers
1134 m_dig_pool.add(dtime);
1135 m_move_pool.add(dtime);
1136 m_time_from_last_punch += dtime;
1137 m_nocheat_dig_time += dtime;
1139 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1140 // If the object gets detached this comes into effect automatically from the last known origin
1143 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1144 m_last_good_position = pos;
1145 m_player->setPosition(pos);
1148 if(send_recommended == false)
1151 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1152 if(m_position_not_sent && !isAttached())
1154 m_position_not_sent = false;
1155 float update_interval = m_env->getSendRecommendedInterval();
1157 if(isAttached()) // Just in case we ever do send attachment position too
1158 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1160 pos = m_player->getPosition() + v3f(0,BS*1,0);
1161 std::string str = gob_cmd_update_position(
1170 // create message and add to list
1171 ActiveObjectMessage aom(getId(), false, str);
1172 m_messages_out.push_back(aom);
1175 if(m_wielded_item_not_sent)
1177 m_wielded_item_not_sent = false;
1178 // GenericCAO has no special way to show this
1181 if(m_armor_groups_sent == false){
1182 m_armor_groups_sent = true;
1183 std::string str = gob_cmd_update_armor_groups(
1185 // create message and add to list
1186 ActiveObjectMessage aom(getId(), true, str);
1187 m_messages_out.push_back(aom);
1190 if(m_physics_override_sent == false){
1191 m_physics_override_sent = true;
1192 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1193 m_physics_override_jump, m_physics_override_gravity,
1194 m_physics_override_sneak, m_physics_override_sneak_glitch);
1195 // create message and add to list
1196 ActiveObjectMessage aom(getId(), true, str);
1197 m_messages_out.push_back(aom);
1200 if(m_animation_sent == false){
1201 m_animation_sent = true;
1202 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1203 // create message and add to list
1204 ActiveObjectMessage aom(getId(), true, str);
1205 m_messages_out.push_back(aom);
1208 if(m_bone_position_sent == false){
1209 m_bone_position_sent = true;
1210 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1211 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1212 // create message and add to list
1213 ActiveObjectMessage aom(getId(), true, str);
1214 m_messages_out.push_back(aom);
1218 if(m_attachment_sent == false){
1219 m_attachment_sent = true;
1220 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1221 // create message and add to list
1222 ActiveObjectMessage aom(getId(), true, str);
1223 m_messages_out.push_back(aom);
1227 void PlayerSAO::setBasePosition(const v3f &position)
1229 // This needs to be ran for attachments too
1230 ServerActiveObject::setBasePosition(position);
1231 m_position_not_sent = true;
1234 void PlayerSAO::setPos(v3f pos)
1238 m_player->setPosition(pos);
1239 // Movement caused by this command is always valid
1240 m_last_good_position = pos;
1241 // Force position change on client
1245 void PlayerSAO::moveTo(v3f pos, bool continuous)
1249 m_player->setPosition(pos);
1250 // Movement caused by this command is always valid
1251 m_last_good_position = pos;
1252 // Force position change on client
1256 void PlayerSAO::setYaw(float yaw)
1258 m_player->setYaw(yaw);
1259 // Force change on client
1263 void PlayerSAO::setPitch(float pitch)
1265 m_player->setPitch(pitch);
1266 // Force change on client
1270 int PlayerSAO::punch(v3f dir,
1271 const ToolCapabilities *toolcap,
1272 ServerActiveObject *puncher,
1273 float time_from_last_punch)
1275 // It's best that attachments cannot be punched
1282 // No effect if PvP disabled
1283 if(g_settings->getBool("enable_pvp") == false){
1284 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1285 std::string str = gob_cmd_punched(0, getHP());
1286 // create message and add to list
1287 ActiveObjectMessage aom(getId(), true, str);
1288 m_messages_out.push_back(aom);
1293 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1294 time_from_last_punch);
1296 std::string punchername = "nil";
1299 punchername = puncher->getDescription();
1301 actionstream<<"Player "<<m_player->getName()<<" punched by "
1302 <<punchername<<", damage "<<hitparams.hp
1305 setHP(getHP() - hitparams.hp);
1307 return hitparams.wear;
1310 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1314 s16 PlayerSAO::getHP() const
1316 return m_player->hp;
1319 s16 PlayerSAO::readDamage()
1321 s16 damage = m_damage;
1326 void PlayerSAO::setHP(s16 hp)
1328 s16 oldhp = m_player->hp;
1332 else if(hp > PLAYER_MAX_HP)
1335 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1337 m_hp_not_sent = true; // fix wrong prediction on client
1344 m_hp_not_sent = true;
1346 m_damage += oldhp - hp;
1349 // Update properties on death
1350 if((hp == 0) != (oldhp == 0))
1351 m_properties_sent = false;
1354 u16 PlayerSAO::getBreath() const
1356 return m_player->getBreath();
1359 void PlayerSAO::setBreath(u16 breath)
1361 m_player->setBreath(breath);
1364 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1366 m_armor_groups = armor_groups;
1367 m_armor_groups_sent = false;
1370 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1372 // store these so they can be updated to clients
1373 m_animation_range = frame_range;
1374 m_animation_speed = frame_speed;
1375 m_animation_blend = frame_blend;
1376 m_animation_sent = false;
1379 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1381 // store these so they can be updated to clients
1382 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1383 m_bone_position_sent = false;
1386 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1388 // Attachments need to be handled on both the server and client.
1389 // If we just attach on the server, we can only copy the position of the parent. Attachments
1390 // are still sent to clients at an interval so players might see them lagging, plus we can't
1391 // read and attach to skeletal bones.
1392 // If we just attach on the client, the server still sees the child at its original location.
1393 // This breaks some things so we also give the server the most accurate representation
1394 // even if players only see the client changes.
1396 m_attachment_parent_id = parent_id;
1397 m_attachment_bone = bone;
1398 m_attachment_position = position;
1399 m_attachment_rotation = rotation;
1400 m_attachment_sent = false;
1403 ObjectProperties* PlayerSAO::accessObjectProperties()
1408 void PlayerSAO::notifyObjectPropertiesModified()
1410 m_properties_sent = false;
1413 Inventory* PlayerSAO::getInventory()
1417 const Inventory* PlayerSAO::getInventory() const
1422 InventoryLocation PlayerSAO::getInventoryLocation() const
1424 InventoryLocation loc;
1425 loc.setPlayer(m_player->getName());
1429 void PlayerSAO::setInventoryModified()
1431 m_inventory_not_sent = true;
1434 std::string PlayerSAO::getWieldList() const
1439 int PlayerSAO::getWieldIndex() const
1441 return m_wield_index;
1444 void PlayerSAO::setWieldIndex(int i)
1446 if(i != m_wield_index)
1449 m_wielded_item_not_sent = true;
1453 void PlayerSAO::disconnected()
1457 if(m_player->getPlayerSAO() == this)
1459 m_player->setPlayerSAO(NULL);
1460 m_player->peer_id = 0;
1464 std::string PlayerSAO::getPropertyPacket()
1466 m_prop.is_visible = (true);
1467 return gob_cmd_set_properties(m_prop);
1470 bool PlayerSAO::checkMovementCheat()
1472 bool cheated = false;
1473 if(isAttached() || m_is_singleplayer ||
1474 g_settings->getBool("disable_anticheat"))
1476 m_last_good_position = m_player->getPosition();
1481 Check player movements
1483 NOTE: Actually the server should handle player physics like the
1484 client does and compare player's position to what is calculated
1485 on our side. This is required when eg. players fly due to an
1486 explosion. Altough a node-based alternative might be possible
1487 too, and much more lightweight.
1490 float player_max_speed = 0;
1491 float player_max_speed_up = 0;
1492 if(m_privs.count("fast") != 0){
1494 player_max_speed = m_player->movement_speed_fast;
1495 player_max_speed_up = m_player->movement_speed_fast;
1498 player_max_speed = m_player->movement_speed_walk;
1499 player_max_speed_up = m_player->movement_speed_walk;
1501 // Tolerance. With the lag pool we shouldn't need it.
1502 //player_max_speed *= 2.5;
1503 //player_max_speed_up *= 2.5;
1505 v3f diff = (m_player->getPosition() - m_last_good_position);
1506 float d_vert = diff.Y;
1508 float d_horiz = diff.getLength();
1509 float required_time = d_horiz/player_max_speed;
1510 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1511 required_time = d_vert/player_max_speed;
1512 if(m_move_pool.grab(required_time)){
1513 m_last_good_position = m_player->getPosition();
1515 actionstream<<"Player "<<m_player->getName()
1516 <<" moved too fast; resetting position"
1518 m_player->setPosition(m_last_good_position);
1526 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1527 //update collision box
1528 *toset = m_player->getCollisionbox();
1530 toset->MinEdge += m_base_position;
1531 toset->MaxEdge += m_base_position;
1536 bool PlayerSAO::collideWithObjects(){