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 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 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1042 std::ostringstream os(std::ios::binary);
1044 if(protocol_version >= 15)
1046 writeU8(os, 1); // version
1047 os<<serializeString(m_player->getName()); // name
1048 writeU8(os, 1); // is_player
1049 writeS16(os, getId()); //id
1050 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1051 writeF1000(os, m_player->getYaw());
1052 writeS16(os, getHP());
1054 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1055 os<<serializeLongString(getPropertyPacket()); // message 1
1056 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1057 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1058 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1059 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1061 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1062 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
1063 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
1064 m_physics_override_sneak_glitch)); // 5
1068 writeU8(os, 0); // version
1069 os<<serializeString(m_player->getName()); // name
1070 writeU8(os, 1); // is_player
1071 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1072 writeF1000(os, m_player->getYaw());
1073 writeS16(os, getHP());
1074 writeU8(os, 2); // number of messages stuffed in here
1075 os<<serializeLongString(getPropertyPacket()); // message 1
1076 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1083 std::string PlayerSAO::getStaticData()
1089 bool PlayerSAO::isAttached()
1091 if(!m_attachment_parent_id)
1093 // Check if the parent still exists
1094 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1100 void PlayerSAO::step(float dtime, bool send_recommended)
1102 if(!m_properties_sent)
1104 m_properties_sent = true;
1105 std::string str = getPropertyPacket();
1106 // create message and add to list
1107 ActiveObjectMessage aom(getId(), true, str);
1108 m_messages_out.push_back(aom);
1111 // If attached, check that our parent is still there. If it isn't, detach.
1112 if(m_attachment_parent_id && !isAttached())
1114 m_attachment_parent_id = 0;
1115 m_attachment_bone = "";
1116 m_attachment_position = v3f(0,0,0);
1117 m_attachment_rotation = v3f(0,0,0);
1118 m_player->setPosition(m_last_good_position);
1122 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1124 // Set lag pool maximums based on estimated lag
1125 const float LAG_POOL_MIN = 5.0;
1126 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1127 if(lag_pool_max < LAG_POOL_MIN)
1128 lag_pool_max = LAG_POOL_MIN;
1129 m_dig_pool.setMax(lag_pool_max);
1130 m_move_pool.setMax(lag_pool_max);
1132 // Increment cheat prevention timers
1133 m_dig_pool.add(dtime);
1134 m_move_pool.add(dtime);
1135 m_time_from_last_punch += dtime;
1136 m_nocheat_dig_time += dtime;
1138 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1139 // If the object gets detached this comes into effect automatically from the last known origin
1142 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1143 m_last_good_position = pos;
1144 m_player->setPosition(pos);
1147 if(send_recommended == false)
1150 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1151 if(m_position_not_sent && !isAttached())
1153 m_position_not_sent = false;
1154 float update_interval = m_env->getSendRecommendedInterval();
1156 if(isAttached()) // Just in case we ever do send attachment position too
1157 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1159 pos = m_player->getPosition() + v3f(0,BS*1,0);
1160 std::string str = gob_cmd_update_position(
1169 // create message and add to list
1170 ActiveObjectMessage aom(getId(), false, str);
1171 m_messages_out.push_back(aom);
1174 if(m_wielded_item_not_sent)
1176 m_wielded_item_not_sent = false;
1177 // GenericCAO has no special way to show this
1180 if(m_armor_groups_sent == false){
1181 m_armor_groups_sent = true;
1182 std::string str = gob_cmd_update_armor_groups(
1184 // create message and add to list
1185 ActiveObjectMessage aom(getId(), true, str);
1186 m_messages_out.push_back(aom);
1189 if(m_physics_override_sent == false){
1190 m_physics_override_sent = true;
1191 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1192 m_physics_override_jump, m_physics_override_gravity,
1193 m_physics_override_sneak, m_physics_override_sneak_glitch);
1194 // create message and add to list
1195 ActiveObjectMessage aom(getId(), true, str);
1196 m_messages_out.push_back(aom);
1199 if(m_animation_sent == false){
1200 m_animation_sent = true;
1201 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1202 // create message and add to list
1203 ActiveObjectMessage aom(getId(), true, str);
1204 m_messages_out.push_back(aom);
1207 if(m_bone_position_sent == false){
1208 m_bone_position_sent = true;
1209 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1210 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1211 // create message and add to list
1212 ActiveObjectMessage aom(getId(), true, str);
1213 m_messages_out.push_back(aom);
1217 if(m_attachment_sent == false){
1218 m_attachment_sent = true;
1219 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1220 // create message and add to list
1221 ActiveObjectMessage aom(getId(), true, str);
1222 m_messages_out.push_back(aom);
1226 void PlayerSAO::setBasePosition(const v3f &position)
1228 // This needs to be ran for attachments too
1229 ServerActiveObject::setBasePosition(position);
1230 m_position_not_sent = true;
1233 void PlayerSAO::setPos(v3f pos)
1237 m_player->setPosition(pos);
1238 // Movement caused by this command is always valid
1239 m_last_good_position = pos;
1240 // Force position change on client
1244 void PlayerSAO::moveTo(v3f pos, bool continuous)
1248 m_player->setPosition(pos);
1249 // Movement caused by this command is always valid
1250 m_last_good_position = pos;
1251 // Force position change on client
1255 void PlayerSAO::setYaw(float yaw)
1257 m_player->setYaw(yaw);
1258 // Force change on client
1262 void PlayerSAO::setPitch(float pitch)
1264 m_player->setPitch(pitch);
1265 // Force change on client
1269 int PlayerSAO::punch(v3f dir,
1270 const ToolCapabilities *toolcap,
1271 ServerActiveObject *puncher,
1272 float time_from_last_punch)
1274 // It's best that attachments cannot be punched
1281 // No effect if PvP disabled
1282 if(g_settings->getBool("enable_pvp") == false){
1283 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1284 std::string str = gob_cmd_punched(0, getHP());
1285 // create message and add to list
1286 ActiveObjectMessage aom(getId(), true, str);
1287 m_messages_out.push_back(aom);
1292 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1293 time_from_last_punch);
1295 std::string punchername = "nil";
1298 punchername = puncher->getDescription();
1300 actionstream<<"Player "<<m_player->getName()<<" punched by "
1301 <<punchername<<", damage "<<hitparams.hp
1304 setHP(getHP() - hitparams.hp);
1306 return hitparams.wear;
1309 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1313 s16 PlayerSAO::getHP() const
1315 return m_player->hp;
1318 s16 PlayerSAO::readDamage()
1320 s16 damage = m_damage;
1325 void PlayerSAO::setHP(s16 hp)
1327 s16 oldhp = m_player->hp;
1331 else if(hp > PLAYER_MAX_HP)
1334 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1336 m_hp_not_sent = true; // fix wrong prediction on client
1343 m_hp_not_sent = true;
1345 m_damage += oldhp - hp;
1348 // Update properties on death
1349 if((hp == 0) != (oldhp == 0))
1350 m_properties_sent = false;
1353 u16 PlayerSAO::getBreath() const
1355 return m_player->getBreath();
1358 void PlayerSAO::setBreath(u16 breath)
1360 m_player->setBreath(breath);
1363 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1365 m_armor_groups = armor_groups;
1366 m_armor_groups_sent = false;
1369 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1371 // store these so they can be updated to clients
1372 m_animation_range = frame_range;
1373 m_animation_speed = frame_speed;
1374 m_animation_blend = frame_blend;
1375 m_animation_sent = false;
1378 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1380 // store these so they can be updated to clients
1381 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1382 m_bone_position_sent = false;
1385 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1387 // Attachments need to be handled on both the server and client.
1388 // If we just attach on the server, we can only copy the position of the parent. Attachments
1389 // are still sent to clients at an interval so players might see them lagging, plus we can't
1390 // read and attach to skeletal bones.
1391 // If we just attach on the client, the server still sees the child at its original location.
1392 // This breaks some things so we also give the server the most accurate representation
1393 // even if players only see the client changes.
1395 m_attachment_parent_id = parent_id;
1396 m_attachment_bone = bone;
1397 m_attachment_position = position;
1398 m_attachment_rotation = rotation;
1399 m_attachment_sent = false;
1402 ObjectProperties* PlayerSAO::accessObjectProperties()
1407 void PlayerSAO::notifyObjectPropertiesModified()
1409 m_properties_sent = false;
1412 Inventory* PlayerSAO::getInventory()
1416 const Inventory* PlayerSAO::getInventory() const
1421 InventoryLocation PlayerSAO::getInventoryLocation() const
1423 InventoryLocation loc;
1424 loc.setPlayer(m_player->getName());
1428 void PlayerSAO::setInventoryModified()
1430 m_inventory_not_sent = true;
1433 std::string PlayerSAO::getWieldList() const
1438 int PlayerSAO::getWieldIndex() const
1440 return m_wield_index;
1443 void PlayerSAO::setWieldIndex(int i)
1445 if(i != m_wield_index)
1448 m_wielded_item_not_sent = true;
1452 void PlayerSAO::disconnected()
1456 if(m_player->getPlayerSAO() == this)
1458 m_player->setPlayerSAO(NULL);
1459 m_player->peer_id = 0;
1463 std::string PlayerSAO::getPropertyPacket()
1465 m_prop.is_visible = (true);
1466 return gob_cmd_set_properties(m_prop);
1469 bool PlayerSAO::checkMovementCheat()
1471 bool cheated = false;
1472 if(isAttached() || m_is_singleplayer ||
1473 g_settings->getBool("disable_anticheat"))
1475 m_last_good_position = m_player->getPosition();
1480 Check player movements
1482 NOTE: Actually the server should handle player physics like the
1483 client does and compare player's position to what is calculated
1484 on our side. This is required when eg. players fly due to an
1485 explosion. Altough a node-based alternative might be possible
1486 too, and much more lightweight.
1489 float player_max_speed = 0;
1490 if(m_privs.count("fast") != 0){
1492 player_max_speed = m_player->movement_speed_fast;
1495 player_max_speed = m_player->movement_speed_walk;
1497 // Tolerance. With the lag pool we shouldn't need it.
1498 //player_max_speed *= 2.5;
1499 //player_max_speed_up *= 2.5;
1501 v3f diff = (m_player->getPosition() - m_last_good_position);
1502 float d_vert = diff.Y;
1504 float d_horiz = diff.getLength();
1505 float required_time = d_horiz/player_max_speed;
1506 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1507 required_time = d_vert/player_max_speed;
1508 if(m_move_pool.grab(required_time)){
1509 m_last_good_position = m_player->getPosition();
1511 actionstream<<"Player "<<m_player->getName()
1512 <<" moved too fast; resetting position"
1514 m_player->setPosition(m_last_good_position);
1522 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1523 //update collision box
1524 *toset = m_player->getCollisionbox();
1526 toset->MinEdge += m_base_position;
1527 toset->MaxEdge += m_base_position;
1532 bool PlayerSAO::collideWithObjects(){