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 f32 stepheight = 0; // Maximum climbable step height
509 v3f p_pos = m_base_position;
510 v3f p_velocity = m_velocity;
511 v3f p_acceleration = m_acceleration;
512 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
513 pos_max_d, box, stepheight, dtime,
514 p_pos, p_velocity, p_acceleration,
515 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_last_good_position_age(0),
938 m_time_from_last_punch(0),
939 m_nocheat_dig_pos(32767, 32767, 32767),
940 m_nocheat_dig_time(0),
942 m_position_not_sent(false),
943 m_armor_groups_sent(false),
944 m_properties_sent(true),
946 m_is_singleplayer(is_singleplayer),
947 m_animation_sent(false),
948 m_bone_position_sent(false),
949 m_attachment_sent(false),
952 m_inventory_not_sent(false),
953 m_hp_not_sent(false),
954 m_breath_not_sent(false),
955 m_wielded_item_not_sent(false),
956 m_physics_override_speed(1),
957 m_physics_override_jump(1),
958 m_physics_override_gravity(1),
959 m_physics_override_sent(false)
962 assert(m_peer_id != 0);
963 setBasePosition(m_player->getPosition());
964 m_inventory = &m_player->inventory;
965 m_armor_groups["fleshy"] = 100;
967 m_prop.hp_max = PLAYER_MAX_HP;
968 m_prop.physical = false;
970 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
971 // start of default appearance, this should be overwritten by LUA
972 m_prop.visual = "upright_sprite";
973 m_prop.visual_size = v2f(1, 2);
974 m_prop.textures.clear();
975 m_prop.textures.push_back("player.png");
976 m_prop.textures.push_back("player_back.png");
977 m_prop.colors.clear();
978 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
979 m_prop.spritediv = v2s16(1,1);
980 // end of default appearance
981 m_prop.is_visible = true;
982 m_prop.makes_footstep_sound = true;
985 PlayerSAO::~PlayerSAO()
987 if(m_inventory != &m_player->inventory)
992 std::string PlayerSAO::getDescription()
994 return std::string("player ") + m_player->getName();
997 // Called after id has been set and has been inserted in environment
998 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1000 ServerActiveObject::addedToEnvironment(dtime_s);
1001 ServerActiveObject::setBasePosition(m_player->getPosition());
1002 m_player->setPlayerSAO(this);
1003 m_player->peer_id = m_peer_id;
1004 m_last_good_position = m_player->getPosition();
1005 m_last_good_position_age = 0.0;
1008 // Called before removing from environment
1009 void PlayerSAO::removingFromEnvironment()
1011 ServerActiveObject::removingFromEnvironment();
1012 if(m_player->getPlayerSAO() == this)
1014 m_player->setPlayerSAO(NULL);
1015 m_player->peer_id = 0;
1019 bool PlayerSAO::isStaticAllowed() const
1024 bool PlayerSAO::unlimitedTransferDistance() const
1026 return g_settings->getBool("unlimited_player_transfer_distance");
1029 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1031 std::ostringstream os(std::ios::binary);
1033 if(protocol_version >= 15)
1035 writeU8(os, 1); // version
1036 os<<serializeString(m_player->getName()); // name
1037 writeU8(os, 1); // is_player
1038 writeS16(os, getId()); //id
1039 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1040 writeF1000(os, m_player->getYaw());
1041 writeS16(os, getHP());
1043 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1044 os<<serializeLongString(getPropertyPacket()); // message 1
1045 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1046 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1047 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1048 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1050 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1051 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1055 writeU8(os, 0); // version
1056 os<<serializeString(m_player->getName()); // name
1057 writeU8(os, 1); // is_player
1058 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1059 writeF1000(os, m_player->getYaw());
1060 writeS16(os, getHP());
1061 writeU8(os, 2); // number of messages stuffed in here
1062 os<<serializeLongString(getPropertyPacket()); // message 1
1063 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1070 std::string PlayerSAO::getStaticData()
1076 bool PlayerSAO::isAttached()
1078 if(!m_attachment_parent_id)
1080 // Check if the parent still exists
1081 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1087 void PlayerSAO::step(float dtime, bool send_recommended)
1089 if(!m_properties_sent)
1091 m_properties_sent = true;
1092 std::string str = getPropertyPacket();
1093 // create message and add to list
1094 ActiveObjectMessage aom(getId(), true, str);
1095 m_messages_out.push_back(aom);
1098 // If attached, check that our parent is still there. If it isn't, detach.
1099 if(m_attachment_parent_id && !isAttached())
1101 m_attachment_parent_id = 0;
1102 m_attachment_bone = "";
1103 m_attachment_position = v3f(0,0,0);
1104 m_attachment_rotation = v3f(0,0,0);
1105 m_player->setPosition(m_last_good_position);
1109 m_time_from_last_punch += dtime;
1110 m_nocheat_dig_time += dtime;
1112 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1113 // If the object gets detached this comes into effect automatically from the last known origin
1116 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1117 m_last_good_position = pos;
1118 m_last_good_position_age = 0;
1119 m_player->setPosition(pos);
1123 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1125 m_last_good_position = m_player->getPosition();
1126 m_last_good_position_age = 0;
1131 Check player movements
1133 NOTE: Actually the server should handle player physics like the
1134 client does and compare player's position to what is calculated
1135 on our side. This is required when eg. players fly due to an
1136 explosion. Altough a node-based alternative might be possible
1137 too, and much more lightweight.
1140 float player_max_speed = 0;
1141 float player_max_speed_up = 0;
1142 if(m_privs.count("fast") != 0){
1144 player_max_speed = BS * 20;
1145 player_max_speed_up = BS * 20;
1148 player_max_speed = BS * 4.0;
1149 player_max_speed_up = BS * 4.0;
1152 player_max_speed *= 2.5;
1153 player_max_speed_up *= 2.5;
1155 m_last_good_position_age += dtime;
1156 if(m_last_good_position_age >= 1.0){
1157 float age = m_last_good_position_age;
1158 v3f diff = (m_player->getPosition() - m_last_good_position);
1159 float d_vert = diff.Y;
1161 float d_horiz = diff.getLength();
1162 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1163 <<(d_horiz/age)<<std::endl;*/
1164 if(d_horiz <= age * player_max_speed &&
1165 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1166 m_last_good_position = m_player->getPosition();
1168 actionstream<<"Player "<<m_player->getName()
1169 <<" moved too fast; resetting position"
1171 m_player->setPosition(m_last_good_position);
1174 m_last_good_position_age = 0;
1179 if(send_recommended == false)
1182 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1183 if(m_position_not_sent && !isAttached())
1185 m_position_not_sent = false;
1186 float update_interval = m_env->getSendRecommendedInterval();
1188 if(isAttached()) // Just in case we ever do send attachment position too
1189 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1191 pos = m_player->getPosition() + v3f(0,BS*1,0);
1192 std::string str = gob_cmd_update_position(
1201 // create message and add to list
1202 ActiveObjectMessage aom(getId(), false, str);
1203 m_messages_out.push_back(aom);
1206 if(m_wielded_item_not_sent)
1208 m_wielded_item_not_sent = false;
1209 // GenericCAO has no special way to show this
1212 if(m_armor_groups_sent == false){
1213 m_armor_groups_sent = true;
1214 std::string str = gob_cmd_update_armor_groups(
1216 // create message and add to list
1217 ActiveObjectMessage aom(getId(), true, str);
1218 m_messages_out.push_back(aom);
1221 if(m_physics_override_sent == false){
1222 m_physics_override_sent = true;
1223 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1224 // create message and add to list
1225 ActiveObjectMessage aom(getId(), true, str);
1226 m_messages_out.push_back(aom);
1229 if(m_animation_sent == false){
1230 m_animation_sent = true;
1231 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1232 // create message and add to list
1233 ActiveObjectMessage aom(getId(), true, str);
1234 m_messages_out.push_back(aom);
1237 if(m_bone_position_sent == false){
1238 m_bone_position_sent = true;
1239 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1240 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1241 // create message and add to list
1242 ActiveObjectMessage aom(getId(), true, str);
1243 m_messages_out.push_back(aom);
1247 if(m_attachment_sent == false){
1248 m_attachment_sent = true;
1249 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1250 // create message and add to list
1251 ActiveObjectMessage aom(getId(), true, str);
1252 m_messages_out.push_back(aom);
1256 void PlayerSAO::setBasePosition(const v3f &position)
1258 // This needs to be ran for attachments too
1259 ServerActiveObject::setBasePosition(position);
1260 m_position_not_sent = true;
1263 void PlayerSAO::setPos(v3f pos)
1267 m_player->setPosition(pos);
1268 // Movement caused by this command is always valid
1269 m_last_good_position = pos;
1270 m_last_good_position_age = 0;
1271 // Force position change on client
1275 void PlayerSAO::moveTo(v3f pos, bool continuous)
1279 m_player->setPosition(pos);
1280 // Movement caused by this command is always valid
1281 m_last_good_position = pos;
1282 m_last_good_position_age = 0;
1283 // Force position change on client
1287 void PlayerSAO::setYaw(float yaw)
1289 m_player->setYaw(yaw);
1290 // Force change on client
1294 void PlayerSAO::setPitch(float pitch)
1296 m_player->setPitch(pitch);
1297 // Force change on client
1301 int PlayerSAO::punch(v3f dir,
1302 const ToolCapabilities *toolcap,
1303 ServerActiveObject *puncher,
1304 float time_from_last_punch)
1306 // It's best that attachments cannot be punched
1313 // No effect if PvP disabled
1314 if(g_settings->getBool("enable_pvp") == false){
1315 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1316 std::string str = gob_cmd_punched(0, getHP());
1317 // create message and add to list
1318 ActiveObjectMessage aom(getId(), true, str);
1319 m_messages_out.push_back(aom);
1324 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1325 time_from_last_punch);
1327 std::string punchername = "nil";
1330 punchername = puncher->getDescription();
1332 actionstream<<"Player "<<m_player->getName()<<" punched by "
1333 <<punchername<<", damage "<<hitparams.hp
1336 setHP(getHP() - hitparams.hp);
1338 if(hitparams.hp != 0)
1340 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1341 // create message and add to list
1342 ActiveObjectMessage aom(getId(), true, str);
1343 m_messages_out.push_back(aom);
1346 return hitparams.wear;
1349 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1353 s16 PlayerSAO::getHP() const
1355 return m_player->hp;
1358 void PlayerSAO::setHP(s16 hp)
1360 s16 oldhp = m_player->hp;
1364 else if(hp > PLAYER_MAX_HP)
1367 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1369 m_hp_not_sent = true; // fix wrong prediction on client
1376 m_hp_not_sent = true;
1378 // On death or reincarnation send an active object message
1379 if((hp == 0) != (oldhp == 0))
1381 // Will send new is_visible value based on (getHP()!=0)
1382 m_properties_sent = false;
1384 std::string str = gob_cmd_punched(0, getHP());
1385 ActiveObjectMessage aom(getId(), true, str);
1386 m_messages_out.push_back(aom);
1390 u16 PlayerSAO::getBreath() const
1392 return m_player->getBreath();
1395 void PlayerSAO::setBreath(u16 breath)
1397 m_player->setBreath(breath);
1400 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1402 m_armor_groups = armor_groups;
1403 m_armor_groups_sent = false;
1406 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1408 // store these so they can be updated to clients
1409 m_animation_range = frame_range;
1410 m_animation_speed = frame_speed;
1411 m_animation_blend = frame_blend;
1412 m_animation_sent = false;
1415 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1417 // store these so they can be updated to clients
1418 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1419 m_bone_position_sent = false;
1422 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1424 // Attachments need to be handled on both the server and client.
1425 // If we just attach on the server, we can only copy the position of the parent. Attachments
1426 // are still sent to clients at an interval so players might see them lagging, plus we can't
1427 // read and attach to skeletal bones.
1428 // If we just attach on the client, the server still sees the child at its original location.
1429 // This breaks some things so we also give the server the most accurate representation
1430 // even if players only see the client changes.
1432 m_attachment_parent_id = parent_id;
1433 m_attachment_bone = bone;
1434 m_attachment_position = position;
1435 m_attachment_rotation = rotation;
1436 m_attachment_sent = false;
1439 ObjectProperties* PlayerSAO::accessObjectProperties()
1444 void PlayerSAO::notifyObjectPropertiesModified()
1446 m_properties_sent = false;
1449 Inventory* PlayerSAO::getInventory()
1453 const Inventory* PlayerSAO::getInventory() const
1458 InventoryLocation PlayerSAO::getInventoryLocation() const
1460 InventoryLocation loc;
1461 loc.setPlayer(m_player->getName());
1465 void PlayerSAO::setInventoryModified()
1467 m_inventory_not_sent = true;
1470 std::string PlayerSAO::getWieldList() const
1475 int PlayerSAO::getWieldIndex() const
1477 return m_wield_index;
1480 void PlayerSAO::setWieldIndex(int i)
1482 if(i != m_wield_index)
1485 m_wielded_item_not_sent = true;
1489 void PlayerSAO::disconnected()
1493 if(m_player->getPlayerSAO() == this)
1495 m_player->setPlayerSAO(NULL);
1496 m_player->peer_id = 0;
1500 std::string PlayerSAO::getPropertyPacket()
1502 m_prop.is_visible = (true);
1503 return gob_cmd_set_properties(m_prop);
1506 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1507 //update collision box
1508 *toset = m_player->getCollisionbox();
1510 toset->MinEdge += m_base_position;
1511 toset->MaxEdge += m_base_position;
1516 bool PlayerSAO::collideWithObjects(){