3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "collision.h"
22 #include "environment.h"
24 #include "main.h" // For g_profiler
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "cpp_api/scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
34 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class DummyLoadSAO : public ServerActiveObject
43 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44 ServerActiveObject(env, pos)
46 ServerActiveObject::registerType(type, create);
48 // Pretend to be the test object (to fool the client)
50 { return ACTIVEOBJECT_TYPE_TEST; }
51 // And never save to disk
52 bool isStaticAllowed() const
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new DummyLoadSAO(env, pos, 0);
61 void step(float dtime, bool send_recommended)
64 infostream<<"DummyLoadSAO step"<<std::endl;
67 bool getCollisionBox(aabb3f *toset) {
71 bool collideWithObjects() {
78 // Prototype (registers item for deserialization)
79 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
80 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
81 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
82 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
88 class TestSAO : public ServerActiveObject
91 TestSAO(ServerEnvironment *env, v3f pos):
92 ServerActiveObject(env, pos),
96 ServerActiveObject::registerType(getType(), create);
99 { return ACTIVEOBJECT_TYPE_TEST; }
101 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
102 const std::string &data)
104 return new TestSAO(env, pos);
107 void step(float dtime, bool send_recommended)
116 m_base_position.Y += dtime * BS * 2;
117 if(m_base_position.Y > 8*BS)
118 m_base_position.Y = 2*BS;
120 if(send_recommended == false)
130 data += itos(0); // 0 = position
132 data += itos(m_base_position.X);
134 data += itos(m_base_position.Y);
136 data += itos(m_base_position.Z);
138 ActiveObjectMessage aom(getId(), false, data);
139 m_messages_out.push_back(aom);
143 bool getCollisionBox(aabb3f *toset) {
147 bool collideWithObjects() {
156 // Prototype (registers item for deserialization)
157 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
162 DEPRECATED: New dropped items are implemented in Lua; see
163 builtin/item_entity.lua.
166 class ItemSAO : public ServerActiveObject
170 { return ACTIVEOBJECT_TYPE_ITEM; }
172 float getMinimumSavedMovement()
175 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
176 const std::string &data)
178 std::istringstream is(data, std::ios::binary);
183 // check if version is supported
186 std::string itemstring = deSerializeString(is);
187 infostream<<"create(): Creating item \""
188 <<itemstring<<"\""<<std::endl;
189 return new ItemSAO(env, pos, itemstring);
192 ItemSAO(ServerEnvironment *env, v3f pos,
193 const std::string itemstring):
194 ServerActiveObject(env, pos),
195 m_itemstring(itemstring),
196 m_itemstring_changed(false),
198 m_last_sent_position(0,0,0)
200 ServerActiveObject::registerType(getType(), create);
203 void step(float dtime, bool send_recommended)
205 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
209 const float interval = 0.2;
210 if(m_move_interval.step(dtime, interval)==false)
214 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
215 collisionMoveResult moveresult;
217 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
218 // Maximum movement without glitches
219 f32 pos_max_d = BS*0.25;
221 if(m_speed_f.getLength()*dtime > pos_max_d)
222 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
223 v3f pos_f = getBasePosition();
224 v3f pos_f_old = pos_f;
225 v3f accel_f = v3f(0,0,0);
227 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
228 pos_max_d, box, stepheight, dtime,
229 pos_f, m_speed_f, accel_f);
231 if(send_recommended == false)
234 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
236 setBasePosition(pos_f);
237 m_last_sent_position = pos_f;
239 std::ostringstream os(std::ios::binary);
240 // command (0 = update position)
243 writeV3F1000(os, m_base_position);
244 // create message and add to list
245 ActiveObjectMessage aom(getId(), false, os.str());
246 m_messages_out.push_back(aom);
248 if(m_itemstring_changed)
250 m_itemstring_changed = false;
252 std::ostringstream os(std::ios::binary);
253 // command (1 = update itemstring)
256 os<<serializeString(m_itemstring);
257 // create message and add to list
258 ActiveObjectMessage aom(getId(), false, os.str());
259 m_messages_out.push_back(aom);
263 std::string getClientInitializationData(u16 protocol_version)
265 std::ostringstream os(std::ios::binary);
269 writeV3F1000(os, m_base_position);
271 os<<serializeString(m_itemstring);
275 std::string getStaticData()
277 infostream<<__FUNCTION_NAME<<std::endl;
278 std::ostringstream os(std::ios::binary);
282 os<<serializeString(m_itemstring);
286 ItemStack createItemStack()
289 IItemDefManager *idef = m_env->getGameDef()->idef();
291 item.deSerialize(m_itemstring, idef);
292 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
293 <<"\" -> item=\""<<item.getItemString()<<"\""
297 catch(SerializationError &e)
299 infostream<<__FUNCTION_NAME<<": serialization error: "
300 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
306 const ToolCapabilities *toolcap,
307 ServerActiveObject *puncher,
308 float time_from_last_punch)
310 // Take item into inventory
311 ItemStack item = createItemStack();
312 Inventory *inv = puncher->getInventory();
315 std::string wieldlist = puncher->getWieldList();
316 ItemStack leftover = inv->addItem(wieldlist, item);
317 puncher->setInventoryModified();
324 m_itemstring = leftover.getItemString();
325 m_itemstring_changed = true;
332 bool getCollisionBox(aabb3f *toset) {
336 bool collideWithObjects() {
341 std::string m_itemstring;
342 bool m_itemstring_changed;
344 v3f m_last_sent_position;
345 IntervalLimiter m_move_interval;
348 // Prototype (registers item for deserialization)
349 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
351 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
352 const std::string itemstring)
354 return new ItemSAO(env, pos, itemstring);
361 // Prototype (registers item for deserialization)
362 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
364 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
365 const std::string &name, const std::string &state):
366 ServerActiveObject(env, pos),
372 m_acceleration(0,0,0),
374 m_properties_sent(true),
376 m_last_sent_position(0,0,0),
377 m_last_sent_velocity(0,0,0),
378 m_last_sent_position_timer(0),
379 m_last_sent_move_precision(0),
380 m_armor_groups_sent(false),
381 m_animation_speed(0),
382 m_animation_blend(0),
383 m_animation_sent(false),
384 m_bone_position_sent(false),
385 m_attachment_parent_id(0),
386 m_attachment_sent(false)
388 // Only register type if no environment supplied
390 ServerActiveObject::registerType(getType(), create);
394 // Initialize something to armor groups
395 m_armor_groups["fleshy"] = 100;
398 LuaEntitySAO::~LuaEntitySAO()
401 ENV_TO_SA(m_env)->luaentity_Remove(m_id);
405 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
407 ServerActiveObject::addedToEnvironment(dtime_s);
409 // Create entity from name
410 m_registered = ENV_TO_SA(m_env)->luaentity_Add(m_id, m_init_name.c_str());
414 ENV_TO_SA(m_env)->luaentity_GetProperties(m_id, &m_prop);
415 // Initialize HP from properties
416 m_hp = m_prop.hp_max;
417 // Activate entity, supplying serialized state
418 ENV_TO_SA(m_env)->luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
422 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
423 const std::string &data)
431 std::istringstream is(data, std::ios::binary);
433 u8 version = readU8(is);
434 // check if version is supported
436 name = deSerializeString(is);
437 state = deSerializeLongString(is);
439 else if(version == 1){
440 name = deSerializeString(is);
441 state = deSerializeLongString(is);
443 velocity = readV3F1000(is);
448 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
449 <<state<<"\")"<<std::endl;
450 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
452 sao->m_velocity = velocity;
457 bool LuaEntitySAO::isAttached()
459 if(!m_attachment_parent_id)
461 // Check if the parent still exists
462 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
468 void LuaEntitySAO::step(float dtime, bool send_recommended)
470 if(!m_properties_sent)
472 m_properties_sent = true;
473 std::string str = getPropertyPacket();
474 // create message and add to list
475 ActiveObjectMessage aom(getId(), true, str);
476 m_messages_out.push_back(aom);
479 // If attached, check that our parent is still there. If it isn't, detach.
480 if(m_attachment_parent_id && !isAttached())
482 m_attachment_parent_id = 0;
483 m_attachment_bone = "";
484 m_attachment_position = v3f(0,0,0);
485 m_attachment_rotation = v3f(0,0,0);
486 sendPosition(false, true);
489 m_last_sent_position_timer += dtime;
491 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
492 // If the object gets detached this comes into effect automatically from the last known origin
495 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
496 m_base_position = pos;
497 m_velocity = v3f(0,0,0);
498 m_acceleration = v3f(0,0,0);
503 core::aabbox3d<f32> box = m_prop.collisionbox;
506 collisionMoveResult moveresult;
507 f32 pos_max_d = BS*0.25; // Distance per iteration
508 v3f p_pos = m_base_position;
509 v3f p_velocity = m_velocity;
510 v3f p_acceleration = m_acceleration;
511 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
512 pos_max_d, box, m_prop.stepheight, dtime,
513 p_pos, p_velocity, p_acceleration,
514 this, m_prop.collideWithObjects);
517 m_base_position = p_pos;
518 m_velocity = p_velocity;
519 m_acceleration = p_acceleration;
521 m_base_position += dtime * m_velocity + 0.5 * dtime
522 * dtime * m_acceleration;
523 m_velocity += dtime * m_acceleration;
528 ENV_TO_SA(m_env)->luaentity_Step(m_id, dtime);
531 if(send_recommended == false)
536 // TODO: force send when acceleration changes enough?
537 float minchange = 0.2*BS;
538 if(m_last_sent_position_timer > 1.0){
540 } else if(m_last_sent_position_timer > 0.2){
543 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
544 move_d += m_last_sent_move_precision;
545 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
546 if(move_d > minchange || vel_d > minchange ||
547 fabs(m_yaw - m_last_sent_yaw) > 1.0){
548 sendPosition(true, false);
552 if(m_armor_groups_sent == false){
553 m_armor_groups_sent = true;
554 std::string str = gob_cmd_update_armor_groups(
556 // create message and add to list
557 ActiveObjectMessage aom(getId(), true, str);
558 m_messages_out.push_back(aom);
561 if(m_animation_sent == false){
562 m_animation_sent = true;
563 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
564 // create message and add to list
565 ActiveObjectMessage aom(getId(), true, str);
566 m_messages_out.push_back(aom);
569 if(m_bone_position_sent == false){
570 m_bone_position_sent = true;
571 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
572 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
573 // create message and add to list
574 ActiveObjectMessage aom(getId(), true, str);
575 m_messages_out.push_back(aom);
579 if(m_attachment_sent == false){
580 m_attachment_sent = true;
581 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
582 // create message and add to list
583 ActiveObjectMessage aom(getId(), true, str);
584 m_messages_out.push_back(aom);
588 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
590 std::ostringstream os(std::ios::binary);
592 if(protocol_version >= 14)
594 writeU8(os, 1); // version
595 os<<serializeString(""); // name
596 writeU8(os, 0); // is_player
597 writeS16(os, getId()); //id
598 writeV3F1000(os, m_base_position);
599 writeF1000(os, m_yaw);
602 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
603 os<<serializeLongString(getPropertyPacket()); // message 1
604 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
605 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
606 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
607 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
609 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
613 writeU8(os, 0); // version
614 os<<serializeString(""); // name
615 writeU8(os, 0); // is_player
616 writeV3F1000(os, m_base_position);
617 writeF1000(os, m_yaw);
619 writeU8(os, 2); // number of messages stuffed in here
620 os<<serializeLongString(getPropertyPacket()); // message 1
621 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
628 std::string LuaEntitySAO::getStaticData()
630 verbosestream<<__FUNCTION_NAME<<std::endl;
631 std::ostringstream os(std::ios::binary);
635 os<<serializeString(m_init_name);
638 std::string state = ENV_TO_SA(m_env)->luaentity_GetStaticdata(m_id);
639 os<<serializeLongString(state);
641 os<<serializeLongString(m_init_state);
646 writeV3F1000(os, m_velocity);
648 writeF1000(os, m_yaw);
652 int LuaEntitySAO::punch(v3f dir,
653 const ToolCapabilities *toolcap,
654 ServerActiveObject *puncher,
655 float time_from_last_punch)
658 // Delete unknown LuaEntities when punched
663 // It's best that attachments cannot be punched
667 ItemStack *punchitem = NULL;
668 ItemStack punchitem_static;
670 punchitem_static = puncher->getWieldedItem();
671 punchitem = &punchitem_static;
674 PunchDamageResult result = getPunchDamage(
678 time_from_last_punch);
682 setHP(getHP() - result.damage);
685 std::string punchername = "nil";
688 punchername = puncher->getDescription();
690 actionstream<<getDescription()<<" punched by "
691 <<punchername<<", damage "<<result.damage
692 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
695 std::string str = gob_cmd_punched(result.damage, getHP());
696 // create message and add to list
697 ActiveObjectMessage aom(getId(), true, str);
698 m_messages_out.push_back(aom);
705 ENV_TO_SA(m_env)->luaentity_Punch(m_id, puncher,
706 time_from_last_punch, toolcap, dir);
711 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
715 // It's best that attachments cannot be clicked
718 ENV_TO_SA(m_env)->luaentity_Rightclick(m_id, clicker);
721 void LuaEntitySAO::setPos(v3f pos)
725 m_base_position = pos;
726 sendPosition(false, true);
729 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
733 m_base_position = pos;
735 sendPosition(true, true);
738 float LuaEntitySAO::getMinimumSavedMovement()
743 std::string LuaEntitySAO::getDescription()
745 std::ostringstream os(std::ios::binary);
746 os<<"LuaEntitySAO at (";
747 os<<(m_base_position.X/BS)<<",";
748 os<<(m_base_position.Y/BS)<<",";
749 os<<(m_base_position.Z/BS);
754 void LuaEntitySAO::setHP(s16 hp)
760 s16 LuaEntitySAO::getHP() const
765 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
767 m_armor_groups = armor_groups;
768 m_armor_groups_sent = false;
771 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
773 m_animation_range = frame_range;
774 m_animation_speed = frame_speed;
775 m_animation_blend = frame_blend;
776 m_animation_sent = false;
779 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
781 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
782 m_bone_position_sent = false;
785 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
787 // Attachments need to be handled on both the server and client.
788 // If we just attach on the server, we can only copy the position of the parent. Attachments
789 // are still sent to clients at an interval so players might see them lagging, plus we can't
790 // read and attach to skeletal bones.
791 // If we just attach on the client, the server still sees the child at its original location.
792 // This breaks some things so we also give the server the most accurate representation
793 // even if players only see the client changes.
795 m_attachment_parent_id = parent_id;
796 m_attachment_bone = bone;
797 m_attachment_position = position;
798 m_attachment_rotation = rotation;
799 m_attachment_sent = false;
802 ObjectProperties* LuaEntitySAO::accessObjectProperties()
807 void LuaEntitySAO::notifyObjectPropertiesModified()
809 m_properties_sent = false;
812 void LuaEntitySAO::setVelocity(v3f velocity)
814 m_velocity = velocity;
817 v3f LuaEntitySAO::getVelocity()
822 void LuaEntitySAO::setAcceleration(v3f acceleration)
824 m_acceleration = acceleration;
827 v3f LuaEntitySAO::getAcceleration()
829 return m_acceleration;
832 void LuaEntitySAO::setYaw(float yaw)
837 float LuaEntitySAO::getYaw()
842 void LuaEntitySAO::setTextureMod(const std::string &mod)
844 std::string str = gob_cmd_set_texture_mod(mod);
845 // create message and add to list
846 ActiveObjectMessage aom(getId(), true, str);
847 m_messages_out.push_back(aom);
850 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
851 bool select_horiz_by_yawpitch)
853 std::string str = gob_cmd_set_sprite(
857 select_horiz_by_yawpitch
859 // create message and add to list
860 ActiveObjectMessage aom(getId(), true, str);
861 m_messages_out.push_back(aom);
864 std::string LuaEntitySAO::getName()
869 std::string LuaEntitySAO::getPropertyPacket()
871 return gob_cmd_set_properties(m_prop);
874 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
876 // If the object is attached client-side, don't waste bandwidth sending its position to clients
880 m_last_sent_move_precision = m_base_position.getDistanceFrom(
881 m_last_sent_position);
882 m_last_sent_position_timer = 0;
883 m_last_sent_yaw = m_yaw;
884 m_last_sent_position = m_base_position;
885 m_last_sent_velocity = m_velocity;
886 //m_last_sent_acceleration = m_acceleration;
888 float update_interval = m_env->getSendRecommendedInterval();
890 std::string str = gob_cmd_update_position(
899 // create message and add to list
900 ActiveObjectMessage aom(getId(), false, str);
901 m_messages_out.push_back(aom);
904 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
907 //update collision box
908 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
909 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
911 toset->MinEdge += m_base_position;
912 toset->MaxEdge += m_base_position;
920 bool LuaEntitySAO::collideWithObjects(){
921 return m_prop.collideWithObjects;
928 // No prototype, PlayerSAO does not need to be deserialized
930 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
931 const std::set<std::string> &privs, bool is_singleplayer):
932 ServerActiveObject(env_, v3f(0,0,0)),
936 m_last_good_position(0,0,0),
937 m_time_from_last_punch(0),
938 m_nocheat_dig_pos(32767, 32767, 32767),
939 m_nocheat_dig_time(0),
941 m_position_not_sent(false),
942 m_armor_groups_sent(false),
943 m_properties_sent(true),
945 m_is_singleplayer(is_singleplayer),
946 m_animation_sent(false),
947 m_bone_position_sent(false),
948 m_attachment_sent(false),
951 m_inventory_not_sent(false),
952 m_hp_not_sent(false),
953 m_breath_not_sent(false),
954 m_wielded_item_not_sent(false),
955 m_physics_override_speed(1),
956 m_physics_override_jump(1),
957 m_physics_override_gravity(1),
958 m_physics_override_sent(false)
961 assert(m_peer_id != 0);
962 setBasePosition(m_player->getPosition());
963 m_inventory = &m_player->inventory;
964 m_armor_groups["fleshy"] = 100;
966 m_prop.hp_max = PLAYER_MAX_HP;
967 m_prop.physical = false;
969 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
970 // start of default appearance, this should be overwritten by LUA
971 m_prop.visual = "upright_sprite";
972 m_prop.visual_size = v2f(1, 2);
973 m_prop.textures.clear();
974 m_prop.textures.push_back("player.png");
975 m_prop.textures.push_back("player_back.png");
976 m_prop.colors.clear();
977 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
978 m_prop.spritediv = v2s16(1,1);
979 // end of default appearance
980 m_prop.is_visible = true;
981 m_prop.makes_footstep_sound = true;
984 PlayerSAO::~PlayerSAO()
986 if(m_inventory != &m_player->inventory)
991 std::string PlayerSAO::getDescription()
993 return std::string("player ") + m_player->getName();
996 // Called after id has been set and has been inserted in environment
997 void PlayerSAO::addedToEnvironment(u32 dtime_s)
999 ServerActiveObject::addedToEnvironment(dtime_s);
1000 ServerActiveObject::setBasePosition(m_player->getPosition());
1001 m_player->setPlayerSAO(this);
1002 m_player->peer_id = m_peer_id;
1003 m_last_good_position = m_player->getPosition();
1006 // Called before removing from environment
1007 void PlayerSAO::removingFromEnvironment()
1009 ServerActiveObject::removingFromEnvironment();
1010 if(m_player->getPlayerSAO() == this)
1012 m_player->setPlayerSAO(NULL);
1013 m_player->peer_id = 0;
1017 bool PlayerSAO::isStaticAllowed() const
1022 bool PlayerSAO::unlimitedTransferDistance() const
1024 return g_settings->getBool("unlimited_player_transfer_distance");
1027 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1029 std::ostringstream os(std::ios::binary);
1031 if(protocol_version >= 15)
1033 writeU8(os, 1); // version
1034 os<<serializeString(m_player->getName()); // name
1035 writeU8(os, 1); // is_player
1036 writeS16(os, getId()); //id
1037 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1038 writeF1000(os, m_player->getYaw());
1039 writeS16(os, getHP());
1041 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1042 os<<serializeLongString(getPropertyPacket()); // message 1
1043 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1044 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1045 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1046 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1048 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1049 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1053 writeU8(os, 0); // version
1054 os<<serializeString(m_player->getName()); // name
1055 writeU8(os, 1); // is_player
1056 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1057 writeF1000(os, m_player->getYaw());
1058 writeS16(os, getHP());
1059 writeU8(os, 2); // number of messages stuffed in here
1060 os<<serializeLongString(getPropertyPacket()); // message 1
1061 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1068 std::string PlayerSAO::getStaticData()
1074 bool PlayerSAO::isAttached()
1076 if(!m_attachment_parent_id)
1078 // Check if the parent still exists
1079 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1085 void PlayerSAO::step(float dtime, bool send_recommended)
1087 if(!m_properties_sent)
1089 m_properties_sent = true;
1090 std::string str = getPropertyPacket();
1091 // create message and add to list
1092 ActiveObjectMessage aom(getId(), true, str);
1093 m_messages_out.push_back(aom);
1096 // If attached, check that our parent is still there. If it isn't, detach.
1097 if(m_attachment_parent_id && !isAttached())
1099 m_attachment_parent_id = 0;
1100 m_attachment_bone = "";
1101 m_attachment_position = v3f(0,0,0);
1102 m_attachment_rotation = v3f(0,0,0);
1103 m_player->setPosition(m_last_good_position);
1107 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1109 // Set lag pool maximums based on estimated lag
1110 const float LAG_POOL_MIN = 5.0;
1111 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1112 if(lag_pool_max < LAG_POOL_MIN)
1113 lag_pool_max = LAG_POOL_MIN;
1114 m_dig_pool.setMax(lag_pool_max);
1115 m_move_pool.setMax(lag_pool_max);
1117 // Increment cheat prevention timers
1118 m_dig_pool.add(dtime);
1119 m_move_pool.add(dtime);
1120 m_time_from_last_punch += dtime;
1121 m_nocheat_dig_time += dtime;
1123 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1124 // If the object gets detached this comes into effect automatically from the last known origin
1127 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1128 m_last_good_position = pos;
1129 m_player->setPosition(pos);
1132 if(send_recommended == false)
1135 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1136 if(m_position_not_sent && !isAttached())
1138 m_position_not_sent = false;
1139 float update_interval = m_env->getSendRecommendedInterval();
1141 if(isAttached()) // Just in case we ever do send attachment position too
1142 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1144 pos = m_player->getPosition() + v3f(0,BS*1,0);
1145 std::string str = gob_cmd_update_position(
1154 // create message and add to list
1155 ActiveObjectMessage aom(getId(), false, str);
1156 m_messages_out.push_back(aom);
1159 if(m_wielded_item_not_sent)
1161 m_wielded_item_not_sent = false;
1162 // GenericCAO has no special way to show this
1165 if(m_armor_groups_sent == false){
1166 m_armor_groups_sent = true;
1167 std::string str = gob_cmd_update_armor_groups(
1169 // create message and add to list
1170 ActiveObjectMessage aom(getId(), true, str);
1171 m_messages_out.push_back(aom);
1174 if(m_physics_override_sent == false){
1175 m_physics_override_sent = true;
1176 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1177 // create message and add to list
1178 ActiveObjectMessage aom(getId(), true, str);
1179 m_messages_out.push_back(aom);
1182 if(m_animation_sent == false){
1183 m_animation_sent = true;
1184 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1185 // create message and add to list
1186 ActiveObjectMessage aom(getId(), true, str);
1187 m_messages_out.push_back(aom);
1190 if(m_bone_position_sent == false){
1191 m_bone_position_sent = true;
1192 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1193 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1194 // create message and add to list
1195 ActiveObjectMessage aom(getId(), true, str);
1196 m_messages_out.push_back(aom);
1200 if(m_attachment_sent == false){
1201 m_attachment_sent = true;
1202 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1203 // create message and add to list
1204 ActiveObjectMessage aom(getId(), true, str);
1205 m_messages_out.push_back(aom);
1209 void PlayerSAO::setBasePosition(const v3f &position)
1211 // This needs to be ran for attachments too
1212 ServerActiveObject::setBasePosition(position);
1213 m_position_not_sent = true;
1216 void PlayerSAO::setPos(v3f pos)
1220 m_player->setPosition(pos);
1221 // Movement caused by this command is always valid
1222 m_last_good_position = pos;
1223 // Force position change on client
1227 void PlayerSAO::moveTo(v3f pos, bool continuous)
1231 m_player->setPosition(pos);
1232 // Movement caused by this command is always valid
1233 m_last_good_position = pos;
1234 // Force position change on client
1238 void PlayerSAO::setYaw(float yaw)
1240 m_player->setYaw(yaw);
1241 // Force change on client
1245 void PlayerSAO::setPitch(float pitch)
1247 m_player->setPitch(pitch);
1248 // Force change on client
1252 int PlayerSAO::punch(v3f dir,
1253 const ToolCapabilities *toolcap,
1254 ServerActiveObject *puncher,
1255 float time_from_last_punch)
1257 // It's best that attachments cannot be punched
1264 // No effect if PvP disabled
1265 if(g_settings->getBool("enable_pvp") == false){
1266 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1267 std::string str = gob_cmd_punched(0, getHP());
1268 // create message and add to list
1269 ActiveObjectMessage aom(getId(), true, str);
1270 m_messages_out.push_back(aom);
1275 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1276 time_from_last_punch);
1278 std::string punchername = "nil";
1281 punchername = puncher->getDescription();
1283 actionstream<<"Player "<<m_player->getName()<<" punched by "
1284 <<punchername<<", damage "<<hitparams.hp
1287 setHP(getHP() - hitparams.hp);
1289 if(hitparams.hp != 0)
1291 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1292 // create message and add to list
1293 ActiveObjectMessage aom(getId(), true, str);
1294 m_messages_out.push_back(aom);
1297 return hitparams.wear;
1300 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1304 s16 PlayerSAO::getHP() const
1306 return m_player->hp;
1309 void PlayerSAO::setHP(s16 hp)
1311 s16 oldhp = m_player->hp;
1315 else if(hp > PLAYER_MAX_HP)
1318 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1320 m_hp_not_sent = true; // fix wrong prediction on client
1327 m_hp_not_sent = true;
1329 // On death or reincarnation send an active object message
1330 if((hp == 0) != (oldhp == 0))
1332 // Will send new is_visible value based on (getHP()!=0)
1333 m_properties_sent = false;
1335 std::string str = gob_cmd_punched(0, getHP());
1336 ActiveObjectMessage aom(getId(), true, str);
1337 m_messages_out.push_back(aom);
1341 u16 PlayerSAO::getBreath() const
1343 return m_player->getBreath();
1346 void PlayerSAO::setBreath(u16 breath)
1348 m_player->setBreath(breath);
1351 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1353 m_armor_groups = armor_groups;
1354 m_armor_groups_sent = false;
1357 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1359 // store these so they can be updated to clients
1360 m_animation_range = frame_range;
1361 m_animation_speed = frame_speed;
1362 m_animation_blend = frame_blend;
1363 m_animation_sent = false;
1366 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1368 // store these so they can be updated to clients
1369 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1370 m_bone_position_sent = false;
1373 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1375 // Attachments need to be handled on both the server and client.
1376 // If we just attach on the server, we can only copy the position of the parent. Attachments
1377 // are still sent to clients at an interval so players might see them lagging, plus we can't
1378 // read and attach to skeletal bones.
1379 // If we just attach on the client, the server still sees the child at its original location.
1380 // This breaks some things so we also give the server the most accurate representation
1381 // even if players only see the client changes.
1383 m_attachment_parent_id = parent_id;
1384 m_attachment_bone = bone;
1385 m_attachment_position = position;
1386 m_attachment_rotation = rotation;
1387 m_attachment_sent = false;
1390 ObjectProperties* PlayerSAO::accessObjectProperties()
1395 void PlayerSAO::notifyObjectPropertiesModified()
1397 m_properties_sent = false;
1400 Inventory* PlayerSAO::getInventory()
1404 const Inventory* PlayerSAO::getInventory() const
1409 InventoryLocation PlayerSAO::getInventoryLocation() const
1411 InventoryLocation loc;
1412 loc.setPlayer(m_player->getName());
1416 void PlayerSAO::setInventoryModified()
1418 m_inventory_not_sent = true;
1421 std::string PlayerSAO::getWieldList() const
1426 int PlayerSAO::getWieldIndex() const
1428 return m_wield_index;
1431 void PlayerSAO::setWieldIndex(int i)
1433 if(i != m_wield_index)
1436 m_wielded_item_not_sent = true;
1440 void PlayerSAO::disconnected()
1444 if(m_player->getPlayerSAO() == this)
1446 m_player->setPlayerSAO(NULL);
1447 m_player->peer_id = 0;
1451 std::string PlayerSAO::getPropertyPacket()
1453 m_prop.is_visible = (true);
1454 return gob_cmd_set_properties(m_prop);
1457 void PlayerSAO::checkMovementCheat()
1459 if(isAttached() || m_is_singleplayer ||
1460 g_settings->getBool("disable_anticheat"))
1462 m_last_good_position = m_player->getPosition();
1467 Check player movements
1469 NOTE: Actually the server should handle player physics like the
1470 client does and compare player's position to what is calculated
1471 on our side. This is required when eg. players fly due to an
1472 explosion. Altough a node-based alternative might be possible
1473 too, and much more lightweight.
1476 float player_max_speed = 0;
1477 float player_max_speed_up = 0;
1478 if(m_privs.count("fast") != 0){
1480 player_max_speed = m_player->movement_speed_fast;
1481 player_max_speed_up = m_player->movement_speed_fast;
1484 player_max_speed = m_player->movement_speed_walk;
1485 player_max_speed_up = m_player->movement_speed_walk;
1487 // Tolerance. With the lag pool we shouldn't need it.
1488 //player_max_speed *= 2.5;
1489 //player_max_speed_up *= 2.5;
1491 v3f diff = (m_player->getPosition() - m_last_good_position);
1492 float d_vert = diff.Y;
1494 float d_horiz = diff.getLength();
1495 float required_time = d_horiz/player_max_speed;
1496 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1497 required_time = d_vert/player_max_speed;
1498 if(m_move_pool.grab(required_time)){
1499 m_last_good_position = m_player->getPosition();
1501 actionstream<<"Player "<<m_player->getName()
1502 <<" moved too fast; resetting position"
1504 m_player->setPosition(m_last_good_position);
1510 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1511 //update collision box
1512 *toset = m_player->getCollisionbox();
1514 toset->MinEdge += m_base_position;
1515 toset->MaxEdge += m_base_position;
1520 bool PlayerSAO::collideWithObjects(){