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_speed(0),
947 m_animation_blend(0),
948 m_animation_sent(false),
949 m_bone_position_sent(false),
950 m_attachment_parent_id(0),
951 m_attachment_sent(false),
954 m_inventory_not_sent(false),
955 m_hp_not_sent(false),
956 m_breath_not_sent(false),
957 m_wielded_item_not_sent(false),
958 m_physics_override_speed(1),
959 m_physics_override_jump(1),
960 m_physics_override_gravity(1),
961 m_physics_override_sent(false)
964 assert(m_peer_id != 0);
965 setBasePosition(m_player->getPosition());
966 m_inventory = &m_player->inventory;
967 m_armor_groups["fleshy"] = 100;
969 m_prop.hp_max = PLAYER_MAX_HP;
970 m_prop.physical = false;
972 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
973 // start of default appearance, this should be overwritten by LUA
974 m_prop.visual = "upright_sprite";
975 m_prop.visual_size = v2f(1, 2);
976 m_prop.textures.clear();
977 m_prop.textures.push_back("player.png");
978 m_prop.textures.push_back("player_back.png");
979 m_prop.colors.clear();
980 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
981 m_prop.spritediv = v2s16(1,1);
982 // end of default appearance
983 m_prop.is_visible = true;
984 m_prop.makes_footstep_sound = true;
987 PlayerSAO::~PlayerSAO()
989 if(m_inventory != &m_player->inventory)
994 std::string PlayerSAO::getDescription()
996 return std::string("player ") + m_player->getName();
999 // Called after id has been set and has been inserted in environment
1000 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1002 ServerActiveObject::addedToEnvironment(dtime_s);
1003 ServerActiveObject::setBasePosition(m_player->getPosition());
1004 m_player->setPlayerSAO(this);
1005 m_player->peer_id = m_peer_id;
1006 m_last_good_position = m_player->getPosition();
1009 // Called before removing from environment
1010 void PlayerSAO::removingFromEnvironment()
1012 ServerActiveObject::removingFromEnvironment();
1013 if(m_player->getPlayerSAO() == this)
1015 m_player->setPlayerSAO(NULL);
1016 m_player->peer_id = 0;
1020 bool PlayerSAO::isStaticAllowed() const
1025 bool PlayerSAO::unlimitedTransferDistance() const
1027 return g_settings->getBool("unlimited_player_transfer_distance");
1030 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1032 std::ostringstream os(std::ios::binary);
1034 if(protocol_version >= 15)
1036 writeU8(os, 1); // version
1037 os<<serializeString(m_player->getName()); // name
1038 writeU8(os, 1); // is_player
1039 writeS16(os, getId()); //id
1040 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1041 writeF1000(os, m_player->getYaw());
1042 writeS16(os, getHP());
1044 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1045 os<<serializeLongString(getPropertyPacket()); // message 1
1046 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1047 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1048 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1049 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1051 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1052 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1056 writeU8(os, 0); // version
1057 os<<serializeString(m_player->getName()); // name
1058 writeU8(os, 1); // is_player
1059 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1060 writeF1000(os, m_player->getYaw());
1061 writeS16(os, getHP());
1062 writeU8(os, 2); // number of messages stuffed in here
1063 os<<serializeLongString(getPropertyPacket()); // message 1
1064 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1071 std::string PlayerSAO::getStaticData()
1077 bool PlayerSAO::isAttached()
1079 if(!m_attachment_parent_id)
1081 // Check if the parent still exists
1082 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1088 void PlayerSAO::step(float dtime, bool send_recommended)
1090 if(!m_properties_sent)
1092 m_properties_sent = true;
1093 std::string str = getPropertyPacket();
1094 // create message and add to list
1095 ActiveObjectMessage aom(getId(), true, str);
1096 m_messages_out.push_back(aom);
1099 // If attached, check that our parent is still there. If it isn't, detach.
1100 if(m_attachment_parent_id && !isAttached())
1102 m_attachment_parent_id = 0;
1103 m_attachment_bone = "";
1104 m_attachment_position = v3f(0,0,0);
1105 m_attachment_rotation = v3f(0,0,0);
1106 m_player->setPosition(m_last_good_position);
1110 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1112 // Set lag pool maximums based on estimated lag
1113 const float LAG_POOL_MIN = 5.0;
1114 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1115 if(lag_pool_max < LAG_POOL_MIN)
1116 lag_pool_max = LAG_POOL_MIN;
1117 m_dig_pool.setMax(lag_pool_max);
1118 m_move_pool.setMax(lag_pool_max);
1120 // Increment cheat prevention timers
1121 m_dig_pool.add(dtime);
1122 m_move_pool.add(dtime);
1123 m_time_from_last_punch += dtime;
1124 m_nocheat_dig_time += dtime;
1126 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1127 // If the object gets detached this comes into effect automatically from the last known origin
1130 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1131 m_last_good_position = pos;
1132 m_player->setPosition(pos);
1135 if(send_recommended == false)
1138 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1139 if(m_position_not_sent && !isAttached())
1141 m_position_not_sent = false;
1142 float update_interval = m_env->getSendRecommendedInterval();
1144 if(isAttached()) // Just in case we ever do send attachment position too
1145 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1147 pos = m_player->getPosition() + v3f(0,BS*1,0);
1148 std::string str = gob_cmd_update_position(
1157 // create message and add to list
1158 ActiveObjectMessage aom(getId(), false, str);
1159 m_messages_out.push_back(aom);
1162 if(m_wielded_item_not_sent)
1164 m_wielded_item_not_sent = false;
1165 // GenericCAO has no special way to show this
1168 if(m_armor_groups_sent == false){
1169 m_armor_groups_sent = true;
1170 std::string str = gob_cmd_update_armor_groups(
1172 // create message and add to list
1173 ActiveObjectMessage aom(getId(), true, str);
1174 m_messages_out.push_back(aom);
1177 if(m_physics_override_sent == false){
1178 m_physics_override_sent = true;
1179 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1180 // create message and add to list
1181 ActiveObjectMessage aom(getId(), true, str);
1182 m_messages_out.push_back(aom);
1185 if(m_animation_sent == false){
1186 m_animation_sent = true;
1187 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1188 // create message and add to list
1189 ActiveObjectMessage aom(getId(), true, str);
1190 m_messages_out.push_back(aom);
1193 if(m_bone_position_sent == false){
1194 m_bone_position_sent = true;
1195 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1196 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1197 // create message and add to list
1198 ActiveObjectMessage aom(getId(), true, str);
1199 m_messages_out.push_back(aom);
1203 if(m_attachment_sent == false){
1204 m_attachment_sent = true;
1205 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1206 // create message and add to list
1207 ActiveObjectMessage aom(getId(), true, str);
1208 m_messages_out.push_back(aom);
1212 void PlayerSAO::setBasePosition(const v3f &position)
1214 // This needs to be ran for attachments too
1215 ServerActiveObject::setBasePosition(position);
1216 m_position_not_sent = true;
1219 void PlayerSAO::setPos(v3f pos)
1223 m_player->setPosition(pos);
1224 // Movement caused by this command is always valid
1225 m_last_good_position = pos;
1226 // Force position change on client
1230 void PlayerSAO::moveTo(v3f pos, bool continuous)
1234 m_player->setPosition(pos);
1235 // Movement caused by this command is always valid
1236 m_last_good_position = pos;
1237 // Force position change on client
1241 void PlayerSAO::setYaw(float yaw)
1243 m_player->setYaw(yaw);
1244 // Force change on client
1248 void PlayerSAO::setPitch(float pitch)
1250 m_player->setPitch(pitch);
1251 // Force change on client
1255 int PlayerSAO::punch(v3f dir,
1256 const ToolCapabilities *toolcap,
1257 ServerActiveObject *puncher,
1258 float time_from_last_punch)
1260 // It's best that attachments cannot be punched
1267 // No effect if PvP disabled
1268 if(g_settings->getBool("enable_pvp") == false){
1269 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1270 std::string str = gob_cmd_punched(0, getHP());
1271 // create message and add to list
1272 ActiveObjectMessage aom(getId(), true, str);
1273 m_messages_out.push_back(aom);
1278 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1279 time_from_last_punch);
1281 std::string punchername = "nil";
1284 punchername = puncher->getDescription();
1286 actionstream<<"Player "<<m_player->getName()<<" punched by "
1287 <<punchername<<", damage "<<hitparams.hp
1290 setHP(getHP() - hitparams.hp);
1292 if(hitparams.hp != 0)
1294 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1295 // create message and add to list
1296 ActiveObjectMessage aom(getId(), true, str);
1297 m_messages_out.push_back(aom);
1300 return hitparams.wear;
1303 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1307 s16 PlayerSAO::getHP() const
1309 return m_player->hp;
1312 void PlayerSAO::setHP(s16 hp)
1314 s16 oldhp = m_player->hp;
1318 else if(hp > PLAYER_MAX_HP)
1321 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1323 m_hp_not_sent = true; // fix wrong prediction on client
1330 m_hp_not_sent = true;
1332 // On death or reincarnation send an active object message
1333 if((hp == 0) != (oldhp == 0))
1335 // Will send new is_visible value based on (getHP()!=0)
1336 m_properties_sent = false;
1338 std::string str = gob_cmd_punched(0, getHP());
1339 ActiveObjectMessage aom(getId(), true, str);
1340 m_messages_out.push_back(aom);
1344 u16 PlayerSAO::getBreath() const
1346 return m_player->getBreath();
1349 void PlayerSAO::setBreath(u16 breath)
1351 m_player->setBreath(breath);
1354 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1356 m_armor_groups = armor_groups;
1357 m_armor_groups_sent = false;
1360 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1362 // store these so they can be updated to clients
1363 m_animation_range = frame_range;
1364 m_animation_speed = frame_speed;
1365 m_animation_blend = frame_blend;
1366 m_animation_sent = false;
1369 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1371 // store these so they can be updated to clients
1372 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1373 m_bone_position_sent = false;
1376 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1378 // Attachments need to be handled on both the server and client.
1379 // If we just attach on the server, we can only copy the position of the parent. Attachments
1380 // are still sent to clients at an interval so players might see them lagging, plus we can't
1381 // read and attach to skeletal bones.
1382 // If we just attach on the client, the server still sees the child at its original location.
1383 // This breaks some things so we also give the server the most accurate representation
1384 // even if players only see the client changes.
1386 m_attachment_parent_id = parent_id;
1387 m_attachment_bone = bone;
1388 m_attachment_position = position;
1389 m_attachment_rotation = rotation;
1390 m_attachment_sent = false;
1393 ObjectProperties* PlayerSAO::accessObjectProperties()
1398 void PlayerSAO::notifyObjectPropertiesModified()
1400 m_properties_sent = false;
1403 Inventory* PlayerSAO::getInventory()
1407 const Inventory* PlayerSAO::getInventory() const
1412 InventoryLocation PlayerSAO::getInventoryLocation() const
1414 InventoryLocation loc;
1415 loc.setPlayer(m_player->getName());
1419 void PlayerSAO::setInventoryModified()
1421 m_inventory_not_sent = true;
1424 std::string PlayerSAO::getWieldList() const
1429 int PlayerSAO::getWieldIndex() const
1431 return m_wield_index;
1434 void PlayerSAO::setWieldIndex(int i)
1436 if(i != m_wield_index)
1439 m_wielded_item_not_sent = true;
1443 void PlayerSAO::disconnected()
1447 if(m_player->getPlayerSAO() == this)
1449 m_player->setPlayerSAO(NULL);
1450 m_player->peer_id = 0;
1454 std::string PlayerSAO::getPropertyPacket()
1456 m_prop.is_visible = (true);
1457 return gob_cmd_set_properties(m_prop);
1460 bool PlayerSAO::checkMovementCheat()
1462 bool cheated = false;
1463 if(isAttached() || m_is_singleplayer ||
1464 g_settings->getBool("disable_anticheat"))
1466 m_last_good_position = m_player->getPosition();
1471 Check player movements
1473 NOTE: Actually the server should handle player physics like the
1474 client does and compare player's position to what is calculated
1475 on our side. This is required when eg. players fly due to an
1476 explosion. Altough a node-based alternative might be possible
1477 too, and much more lightweight.
1480 float player_max_speed = 0;
1481 float player_max_speed_up = 0;
1482 if(m_privs.count("fast") != 0){
1484 player_max_speed = m_player->movement_speed_fast;
1485 player_max_speed_up = m_player->movement_speed_fast;
1488 player_max_speed = m_player->movement_speed_walk;
1489 player_max_speed_up = m_player->movement_speed_walk;
1491 // Tolerance. With the lag pool we shouldn't need it.
1492 //player_max_speed *= 2.5;
1493 //player_max_speed_up *= 2.5;
1495 v3f diff = (m_player->getPosition() - m_last_good_position);
1496 float d_vert = diff.Y;
1498 float d_horiz = diff.getLength();
1499 float required_time = d_horiz/player_max_speed;
1500 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1501 required_time = d_vert/player_max_speed;
1502 if(m_move_pool.grab(required_time)){
1503 m_last_good_position = m_player->getPosition();
1505 actionstream<<"Player "<<m_player->getName()
1506 <<" moved too fast; resetting position"
1508 m_player->setPosition(m_last_good_position);
1516 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1517 //update collision box
1518 *toset = m_player->getCollisionbox();
1520 toset->MinEdge += m_base_position;
1521 toset->MaxEdge += m_base_position;
1526 bool PlayerSAO::collideWithObjects(){