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 "scripting_game.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
33 #include "util/mathconstants.h"
35 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
41 class DummyLoadSAO : public ServerActiveObject
44 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
45 ServerActiveObject(env, pos)
47 ServerActiveObject::registerType(type, create);
49 // Pretend to be the test object (to fool the client)
51 { return ACTIVEOBJECT_TYPE_TEST; }
52 // And never save to disk
53 bool isStaticAllowed() const
56 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
57 const std::string &data)
59 return new DummyLoadSAO(env, pos, 0);
62 void step(float dtime, bool send_recommended)
65 infostream<<"DummyLoadSAO step"<<std::endl;
68 bool getCollisionBox(aabb3f *toset) {
72 bool collideWithObjects() {
79 // Prototype (registers item for deserialization)
80 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
81 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
82 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
83 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
89 class TestSAO : public ServerActiveObject
92 TestSAO(ServerEnvironment *env, v3f pos):
93 ServerActiveObject(env, pos),
97 ServerActiveObject::registerType(getType(), create);
100 { return ACTIVEOBJECT_TYPE_TEST; }
102 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
103 const std::string &data)
105 return new TestSAO(env, pos);
108 void step(float dtime, bool send_recommended)
117 m_base_position.Y += dtime * BS * 2;
118 if(m_base_position.Y > 8*BS)
119 m_base_position.Y = 2*BS;
121 if(send_recommended == false)
131 data += itos(0); // 0 = position
133 data += itos(m_base_position.X);
135 data += itos(m_base_position.Y);
137 data += itos(m_base_position.Z);
139 ActiveObjectMessage aom(getId(), false, data);
140 m_messages_out.push_back(aom);
144 bool getCollisionBox(aabb3f *toset) {
148 bool collideWithObjects() {
157 // Prototype (registers item for deserialization)
158 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
163 DEPRECATED: New dropped items are implemented in Lua; see
164 builtin/item_entity.lua.
167 class ItemSAO : public ServerActiveObject
171 { return ACTIVEOBJECT_TYPE_ITEM; }
173 float getMinimumSavedMovement()
176 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
177 const std::string &data)
179 std::istringstream is(data, std::ios::binary);
184 // check if version is supported
187 std::string itemstring = deSerializeString(is);
188 infostream<<"create(): Creating item \""
189 <<itemstring<<"\""<<std::endl;
190 return new ItemSAO(env, pos, itemstring);
193 ItemSAO(ServerEnvironment *env, v3f pos,
194 const std::string itemstring):
195 ServerActiveObject(env, pos),
196 m_itemstring(itemstring),
197 m_itemstring_changed(false),
199 m_last_sent_position(0,0,0)
201 ServerActiveObject::registerType(getType(), create);
204 void step(float dtime, bool send_recommended)
206 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
210 const float interval = 0.2;
211 if(m_move_interval.step(dtime, interval)==false)
215 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
216 collisionMoveResult moveresult;
218 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
219 // Maximum movement without glitches
220 f32 pos_max_d = BS*0.25;
222 if(m_speed_f.getLength()*dtime > pos_max_d)
223 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
224 v3f pos_f = getBasePosition();
225 v3f pos_f_old = pos_f;
226 v3f accel_f = v3f(0,0,0);
228 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
229 pos_max_d, box, stepheight, dtime,
230 pos_f, m_speed_f, accel_f);
232 if(send_recommended == false)
235 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
237 setBasePosition(pos_f);
238 m_last_sent_position = pos_f;
240 std::ostringstream os(std::ios::binary);
241 // command (0 = update position)
244 writeV3F1000(os, m_base_position);
245 // create message and add to list
246 ActiveObjectMessage aom(getId(), false, os.str());
247 m_messages_out.push_back(aom);
249 if(m_itemstring_changed)
251 m_itemstring_changed = false;
253 std::ostringstream os(std::ios::binary);
254 // command (1 = update itemstring)
257 os<<serializeString(m_itemstring);
258 // create message and add to list
259 ActiveObjectMessage aom(getId(), false, os.str());
260 m_messages_out.push_back(aom);
264 std::string getClientInitializationData(u16 protocol_version)
266 std::ostringstream os(std::ios::binary);
270 writeV3F1000(os, m_base_position);
272 os<<serializeString(m_itemstring);
276 std::string getStaticData()
278 infostream<<__FUNCTION_NAME<<std::endl;
279 std::ostringstream os(std::ios::binary);
283 os<<serializeString(m_itemstring);
287 ItemStack createItemStack()
290 IItemDefManager *idef = m_env->getGameDef()->idef();
292 item.deSerialize(m_itemstring, idef);
293 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
294 <<"\" -> item=\""<<item.getItemString()<<"\""
298 catch(SerializationError &e)
300 infostream<<__FUNCTION_NAME<<": serialization error: "
301 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
307 const ToolCapabilities *toolcap,
308 ServerActiveObject *puncher,
309 float time_from_last_punch)
311 // Take item into inventory
312 ItemStack item = createItemStack();
313 Inventory *inv = puncher->getInventory();
316 std::string wieldlist = puncher->getWieldList();
317 ItemStack leftover = inv->addItem(wieldlist, item);
318 puncher->setInventoryModified();
325 m_itemstring = leftover.getItemString();
326 m_itemstring_changed = true;
333 bool getCollisionBox(aabb3f *toset) {
337 bool collideWithObjects() {
342 std::string m_itemstring;
343 bool m_itemstring_changed;
345 v3f m_last_sent_position;
346 IntervalLimiter m_move_interval;
349 // Prototype (registers item for deserialization)
350 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
352 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
353 const std::string itemstring)
355 return new ItemSAO(env, pos, itemstring);
362 // Prototype (registers item for deserialization)
363 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
365 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
366 const std::string &name, const std::string &state):
367 ServerActiveObject(env, pos),
373 m_acceleration(0,0,0),
375 m_properties_sent(true),
377 m_last_sent_position(0,0,0),
378 m_last_sent_velocity(0,0,0),
379 m_last_sent_position_timer(0),
380 m_last_sent_move_precision(0),
381 m_armor_groups_sent(false),
382 m_animation_speed(0),
383 m_animation_blend(0),
384 m_animation_sent(false),
385 m_bone_position_sent(false),
386 m_attachment_parent_id(0),
387 m_attachment_sent(false)
389 // Only register type if no environment supplied
391 ServerActiveObject::registerType(getType(), create);
395 // Initialize something to armor groups
396 m_armor_groups["fleshy"] = 100;
399 LuaEntitySAO::~LuaEntitySAO()
402 m_env->getScriptIface()->luaentity_Remove(m_id);
406 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
408 ServerActiveObject::addedToEnvironment(dtime_s);
410 // Create entity from name
411 m_registered = m_env->getScriptIface()->
412 luaentity_Add(m_id, m_init_name.c_str());
416 m_env->getScriptIface()->
417 luaentity_GetProperties(m_id, &m_prop);
418 // Initialize HP from properties
419 m_hp = m_prop.hp_max;
420 // Activate entity, supplying serialized state
421 m_env->getScriptIface()->
422 luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
426 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
427 const std::string &data)
435 std::istringstream is(data, std::ios::binary);
437 u8 version = readU8(is);
438 // check if version is supported
440 name = deSerializeString(is);
441 state = deSerializeLongString(is);
443 else if(version == 1){
444 name = deSerializeString(is);
445 state = deSerializeLongString(is);
447 velocity = readV3F1000(is);
452 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
453 <<state<<"\")"<<std::endl;
454 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
456 sao->m_velocity = velocity;
461 bool LuaEntitySAO::isAttached()
463 if(!m_attachment_parent_id)
465 // Check if the parent still exists
466 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
472 void LuaEntitySAO::step(float dtime, bool send_recommended)
474 if(!m_properties_sent)
476 m_properties_sent = true;
477 std::string str = getPropertyPacket();
478 // create message and add to list
479 ActiveObjectMessage aom(getId(), true, str);
480 m_messages_out.push_back(aom);
483 // If attached, check that our parent is still there. If it isn't, detach.
484 if(m_attachment_parent_id && !isAttached())
486 m_attachment_parent_id = 0;
487 m_attachment_bone = "";
488 m_attachment_position = v3f(0,0,0);
489 m_attachment_rotation = v3f(0,0,0);
490 sendPosition(false, true);
493 m_last_sent_position_timer += dtime;
495 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
496 // If the object gets detached this comes into effect automatically from the last known origin
499 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
500 m_base_position = pos;
501 m_velocity = v3f(0,0,0);
502 m_acceleration = v3f(0,0,0);
507 core::aabbox3d<f32> box = m_prop.collisionbox;
510 collisionMoveResult moveresult;
511 f32 pos_max_d = BS*0.25; // Distance per iteration
512 v3f p_pos = m_base_position;
513 v3f p_velocity = m_velocity;
514 v3f p_acceleration = m_acceleration;
515 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
516 pos_max_d, box, m_prop.stepheight, dtime,
517 p_pos, p_velocity, p_acceleration,
518 this, m_prop.collideWithObjects);
521 m_base_position = p_pos;
522 m_velocity = p_velocity;
523 m_acceleration = p_acceleration;
525 m_base_position += dtime * m_velocity + 0.5 * dtime
526 * dtime * m_acceleration;
527 m_velocity += dtime * m_acceleration;
530 if(m_prop.automatic_face_movement_dir){
531 m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI;
536 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
539 if(send_recommended == false)
544 // TODO: force send when acceleration changes enough?
545 float minchange = 0.2*BS;
546 if(m_last_sent_position_timer > 1.0){
548 } else if(m_last_sent_position_timer > 0.2){
551 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
552 move_d += m_last_sent_move_precision;
553 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
554 if(move_d > minchange || vel_d > minchange ||
555 fabs(m_yaw - m_last_sent_yaw) > 1.0){
556 sendPosition(true, false);
560 if(m_armor_groups_sent == false){
561 m_armor_groups_sent = true;
562 std::string str = gob_cmd_update_armor_groups(
564 // create message and add to list
565 ActiveObjectMessage aom(getId(), true, str);
566 m_messages_out.push_back(aom);
569 if(m_animation_sent == false){
570 m_animation_sent = true;
571 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
572 // create message and add to list
573 ActiveObjectMessage aom(getId(), true, str);
574 m_messages_out.push_back(aom);
577 if(m_bone_position_sent == false){
578 m_bone_position_sent = true;
579 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
580 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
581 // create message and add to list
582 ActiveObjectMessage aom(getId(), true, str);
583 m_messages_out.push_back(aom);
587 if(m_attachment_sent == false){
588 m_attachment_sent = true;
589 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
590 // create message and add to list
591 ActiveObjectMessage aom(getId(), true, str);
592 m_messages_out.push_back(aom);
596 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
598 std::ostringstream os(std::ios::binary);
600 if(protocol_version >= 14)
602 writeU8(os, 1); // version
603 os<<serializeString(""); // name
604 writeU8(os, 0); // is_player
605 writeS16(os, getId()); //id
606 writeV3F1000(os, m_base_position);
607 writeF1000(os, m_yaw);
610 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
611 os<<serializeLongString(getPropertyPacket()); // message 1
612 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
613 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
614 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
615 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
617 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
621 writeU8(os, 0); // version
622 os<<serializeString(""); // name
623 writeU8(os, 0); // is_player
624 writeV3F1000(os, m_base_position);
625 writeF1000(os, m_yaw);
627 writeU8(os, 2); // number of messages stuffed in here
628 os<<serializeLongString(getPropertyPacket()); // message 1
629 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
636 std::string LuaEntitySAO::getStaticData()
638 verbosestream<<__FUNCTION_NAME<<std::endl;
639 std::ostringstream os(std::ios::binary);
643 os<<serializeString(m_init_name);
646 std::string state = m_env->getScriptIface()->
647 luaentity_GetStaticdata(m_id);
648 os<<serializeLongString(state);
650 os<<serializeLongString(m_init_state);
655 writeV3F1000(os, m_velocity);
657 writeF1000(os, m_yaw);
661 int LuaEntitySAO::punch(v3f dir,
662 const ToolCapabilities *toolcap,
663 ServerActiveObject *puncher,
664 float time_from_last_punch)
667 // Delete unknown LuaEntities when punched
672 // It's best that attachments cannot be punched
676 ItemStack *punchitem = NULL;
677 ItemStack punchitem_static;
679 punchitem_static = puncher->getWieldedItem();
680 punchitem = &punchitem_static;
683 PunchDamageResult result = getPunchDamage(
687 time_from_last_punch);
691 setHP(getHP() - result.damage);
694 std::string punchername = "nil";
697 punchername = puncher->getDescription();
699 actionstream<<getDescription()<<" punched by "
700 <<punchername<<", damage "<<result.damage
701 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
704 std::string str = gob_cmd_punched(result.damage, getHP());
705 // create message and add to list
706 ActiveObjectMessage aom(getId(), true, str);
707 m_messages_out.push_back(aom);
714 m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
715 time_from_last_punch, toolcap, dir);
720 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
724 // It's best that attachments cannot be clicked
727 m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
730 void LuaEntitySAO::setPos(v3f pos)
734 m_base_position = pos;
735 sendPosition(false, true);
738 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
742 m_base_position = pos;
744 sendPosition(true, true);
747 float LuaEntitySAO::getMinimumSavedMovement()
752 std::string LuaEntitySAO::getDescription()
754 std::ostringstream os(std::ios::binary);
755 os<<"LuaEntitySAO at (";
756 os<<(m_base_position.X/BS)<<",";
757 os<<(m_base_position.Y/BS)<<",";
758 os<<(m_base_position.Z/BS);
763 void LuaEntitySAO::setHP(s16 hp)
769 s16 LuaEntitySAO::getHP() const
774 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
776 m_armor_groups = armor_groups;
777 m_armor_groups_sent = false;
780 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
782 m_animation_range = frame_range;
783 m_animation_speed = frame_speed;
784 m_animation_blend = frame_blend;
785 m_animation_sent = false;
788 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
790 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
791 m_bone_position_sent = false;
794 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
796 // Attachments need to be handled on both the server and client.
797 // If we just attach on the server, we can only copy the position of the parent. Attachments
798 // are still sent to clients at an interval so players might see them lagging, plus we can't
799 // read and attach to skeletal bones.
800 // If we just attach on the client, the server still sees the child at its original location.
801 // This breaks some things so we also give the server the most accurate representation
802 // even if players only see the client changes.
804 m_attachment_parent_id = parent_id;
805 m_attachment_bone = bone;
806 m_attachment_position = position;
807 m_attachment_rotation = rotation;
808 m_attachment_sent = false;
811 ObjectProperties* LuaEntitySAO::accessObjectProperties()
816 void LuaEntitySAO::notifyObjectPropertiesModified()
818 m_properties_sent = false;
821 void LuaEntitySAO::setVelocity(v3f velocity)
823 m_velocity = velocity;
826 v3f LuaEntitySAO::getVelocity()
831 void LuaEntitySAO::setAcceleration(v3f acceleration)
833 m_acceleration = acceleration;
836 v3f LuaEntitySAO::getAcceleration()
838 return m_acceleration;
841 void LuaEntitySAO::setYaw(float yaw)
846 float LuaEntitySAO::getYaw()
851 void LuaEntitySAO::setTextureMod(const std::string &mod)
853 std::string str = gob_cmd_set_texture_mod(mod);
854 // create message and add to list
855 ActiveObjectMessage aom(getId(), true, str);
856 m_messages_out.push_back(aom);
859 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
860 bool select_horiz_by_yawpitch)
862 std::string str = gob_cmd_set_sprite(
866 select_horiz_by_yawpitch
868 // create message and add to list
869 ActiveObjectMessage aom(getId(), true, str);
870 m_messages_out.push_back(aom);
873 std::string LuaEntitySAO::getName()
878 std::string LuaEntitySAO::getPropertyPacket()
880 return gob_cmd_set_properties(m_prop);
883 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
885 // If the object is attached client-side, don't waste bandwidth sending its position to clients
889 m_last_sent_move_precision = m_base_position.getDistanceFrom(
890 m_last_sent_position);
891 m_last_sent_position_timer = 0;
892 m_last_sent_yaw = m_yaw;
893 m_last_sent_position = m_base_position;
894 m_last_sent_velocity = m_velocity;
895 //m_last_sent_acceleration = m_acceleration;
897 float update_interval = m_env->getSendRecommendedInterval();
899 std::string str = gob_cmd_update_position(
908 // create message and add to list
909 ActiveObjectMessage aom(getId(), false, str);
910 m_messages_out.push_back(aom);
913 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
916 //update collision box
917 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
918 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
920 toset->MinEdge += m_base_position;
921 toset->MaxEdge += m_base_position;
929 bool LuaEntitySAO::collideWithObjects(){
930 return m_prop.collideWithObjects;
937 // No prototype, PlayerSAO does not need to be deserialized
939 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
940 const std::set<std::string> &privs, bool is_singleplayer):
941 ServerActiveObject(env_, v3f(0,0,0)),
946 m_last_good_position(0,0,0),
947 m_time_from_last_punch(0),
948 m_nocheat_dig_pos(32767, 32767, 32767),
949 m_nocheat_dig_time(0),
951 m_position_not_sent(false),
952 m_armor_groups_sent(false),
953 m_properties_sent(true),
955 m_is_singleplayer(is_singleplayer),
956 m_animation_speed(0),
957 m_animation_blend(0),
958 m_animation_sent(false),
959 m_bone_position_sent(false),
960 m_attachment_parent_id(0),
961 m_attachment_sent(false),
964 m_inventory_not_sent(false),
965 m_hp_not_sent(false),
966 m_breath_not_sent(false),
967 m_wielded_item_not_sent(false),
968 m_physics_override_speed(1),
969 m_physics_override_jump(1),
970 m_physics_override_gravity(1),
971 m_physics_override_sent(false)
974 assert(m_peer_id != 0);
975 setBasePosition(m_player->getPosition());
976 m_inventory = &m_player->inventory;
977 m_armor_groups["fleshy"] = 100;
979 m_prop.hp_max = PLAYER_MAX_HP;
980 m_prop.physical = false;
982 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
983 // start of default appearance, this should be overwritten by LUA
984 m_prop.visual = "upright_sprite";
985 m_prop.visual_size = v2f(1, 2);
986 m_prop.textures.clear();
987 m_prop.textures.push_back("player.png");
988 m_prop.textures.push_back("player_back.png");
989 m_prop.colors.clear();
990 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
991 m_prop.spritediv = v2s16(1,1);
992 // end of default appearance
993 m_prop.is_visible = true;
994 m_prop.makes_footstep_sound = true;
997 PlayerSAO::~PlayerSAO()
999 if(m_inventory != &m_player->inventory)
1004 std::string PlayerSAO::getDescription()
1006 return std::string("player ") + m_player->getName();
1009 // Called after id has been set and has been inserted in environment
1010 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1012 ServerActiveObject::addedToEnvironment(dtime_s);
1013 ServerActiveObject::setBasePosition(m_player->getPosition());
1014 m_player->setPlayerSAO(this);
1015 m_player->peer_id = m_peer_id;
1016 m_last_good_position = m_player->getPosition();
1019 // Called before removing from environment
1020 void PlayerSAO::removingFromEnvironment()
1022 ServerActiveObject::removingFromEnvironment();
1023 if(m_player->getPlayerSAO() == this)
1025 m_player->setPlayerSAO(NULL);
1026 m_player->peer_id = 0;
1030 bool PlayerSAO::isStaticAllowed() const
1035 bool PlayerSAO::unlimitedTransferDistance() const
1037 return g_settings->getBool("unlimited_player_transfer_distance");
1040 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1042 std::ostringstream os(std::ios::binary);
1044 if(protocol_version >= 15)
1046 writeU8(os, 1); // version
1047 os<<serializeString(m_player->getName()); // name
1048 writeU8(os, 1); // is_player
1049 writeS16(os, getId()); //id
1050 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1051 writeF1000(os, m_player->getYaw());
1052 writeS16(os, getHP());
1054 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1055 os<<serializeLongString(getPropertyPacket()); // message 1
1056 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1057 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1058 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1059 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1061 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1062 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1066 writeU8(os, 0); // version
1067 os<<serializeString(m_player->getName()); // name
1068 writeU8(os, 1); // is_player
1069 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1070 writeF1000(os, m_player->getYaw());
1071 writeS16(os, getHP());
1072 writeU8(os, 2); // number of messages stuffed in here
1073 os<<serializeLongString(getPropertyPacket()); // message 1
1074 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1081 std::string PlayerSAO::getStaticData()
1087 bool PlayerSAO::isAttached()
1089 if(!m_attachment_parent_id)
1091 // Check if the parent still exists
1092 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1098 void PlayerSAO::step(float dtime, bool send_recommended)
1100 if(!m_properties_sent)
1102 m_properties_sent = true;
1103 std::string str = getPropertyPacket();
1104 // create message and add to list
1105 ActiveObjectMessage aom(getId(), true, str);
1106 m_messages_out.push_back(aom);
1109 // If attached, check that our parent is still there. If it isn't, detach.
1110 if(m_attachment_parent_id && !isAttached())
1112 m_attachment_parent_id = 0;
1113 m_attachment_bone = "";
1114 m_attachment_position = v3f(0,0,0);
1115 m_attachment_rotation = v3f(0,0,0);
1116 m_player->setPosition(m_last_good_position);
1120 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1122 // Set lag pool maximums based on estimated lag
1123 const float LAG_POOL_MIN = 5.0;
1124 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1125 if(lag_pool_max < LAG_POOL_MIN)
1126 lag_pool_max = LAG_POOL_MIN;
1127 m_dig_pool.setMax(lag_pool_max);
1128 m_move_pool.setMax(lag_pool_max);
1130 // Increment cheat prevention timers
1131 m_dig_pool.add(dtime);
1132 m_move_pool.add(dtime);
1133 m_time_from_last_punch += dtime;
1134 m_nocheat_dig_time += dtime;
1136 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1137 // If the object gets detached this comes into effect automatically from the last known origin
1140 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1141 m_last_good_position = pos;
1142 m_player->setPosition(pos);
1145 if(send_recommended == false)
1148 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1149 if(m_position_not_sent && !isAttached())
1151 m_position_not_sent = false;
1152 float update_interval = m_env->getSendRecommendedInterval();
1154 if(isAttached()) // Just in case we ever do send attachment position too
1155 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1157 pos = m_player->getPosition() + v3f(0,BS*1,0);
1158 std::string str = gob_cmd_update_position(
1167 // create message and add to list
1168 ActiveObjectMessage aom(getId(), false, str);
1169 m_messages_out.push_back(aom);
1172 if(m_wielded_item_not_sent)
1174 m_wielded_item_not_sent = false;
1175 // GenericCAO has no special way to show this
1178 if(m_armor_groups_sent == false){
1179 m_armor_groups_sent = true;
1180 std::string str = gob_cmd_update_armor_groups(
1182 // create message and add to list
1183 ActiveObjectMessage aom(getId(), true, str);
1184 m_messages_out.push_back(aom);
1187 if(m_physics_override_sent == false){
1188 m_physics_override_sent = true;
1189 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1190 // create message and add to list
1191 ActiveObjectMessage aom(getId(), true, str);
1192 m_messages_out.push_back(aom);
1195 if(m_animation_sent == false){
1196 m_animation_sent = true;
1197 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1198 // create message and add to list
1199 ActiveObjectMessage aom(getId(), true, str);
1200 m_messages_out.push_back(aom);
1203 if(m_bone_position_sent == false){
1204 m_bone_position_sent = true;
1205 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1206 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1207 // create message and add to list
1208 ActiveObjectMessage aom(getId(), true, str);
1209 m_messages_out.push_back(aom);
1213 if(m_attachment_sent == false){
1214 m_attachment_sent = true;
1215 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1216 // create message and add to list
1217 ActiveObjectMessage aom(getId(), true, str);
1218 m_messages_out.push_back(aom);
1222 void PlayerSAO::setBasePosition(const v3f &position)
1224 // This needs to be ran for attachments too
1225 ServerActiveObject::setBasePosition(position);
1226 m_position_not_sent = true;
1229 void PlayerSAO::setPos(v3f pos)
1233 m_player->setPosition(pos);
1234 // Movement caused by this command is always valid
1235 m_last_good_position = pos;
1236 // Force position change on client
1240 void PlayerSAO::moveTo(v3f pos, bool continuous)
1244 m_player->setPosition(pos);
1245 // Movement caused by this command is always valid
1246 m_last_good_position = pos;
1247 // Force position change on client
1251 void PlayerSAO::setYaw(float yaw)
1253 m_player->setYaw(yaw);
1254 // Force change on client
1258 void PlayerSAO::setPitch(float pitch)
1260 m_player->setPitch(pitch);
1261 // Force change on client
1265 int PlayerSAO::punch(v3f dir,
1266 const ToolCapabilities *toolcap,
1267 ServerActiveObject *puncher,
1268 float time_from_last_punch)
1270 // It's best that attachments cannot be punched
1277 // No effect if PvP disabled
1278 if(g_settings->getBool("enable_pvp") == false){
1279 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1280 std::string str = gob_cmd_punched(0, getHP());
1281 // create message and add to list
1282 ActiveObjectMessage aom(getId(), true, str);
1283 m_messages_out.push_back(aom);
1288 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1289 time_from_last_punch);
1291 std::string punchername = "nil";
1294 punchername = puncher->getDescription();
1296 actionstream<<"Player "<<m_player->getName()<<" punched by "
1297 <<punchername<<", damage "<<hitparams.hp
1300 setHP(getHP() - hitparams.hp);
1302 return hitparams.wear;
1305 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1309 s16 PlayerSAO::getHP() const
1311 return m_player->hp;
1314 s16 PlayerSAO::readDamage()
1316 s16 damage = m_damage;
1321 void PlayerSAO::setHP(s16 hp)
1323 s16 oldhp = m_player->hp;
1327 else if(hp > PLAYER_MAX_HP)
1330 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1332 m_hp_not_sent = true; // fix wrong prediction on client
1339 m_hp_not_sent = true;
1341 m_damage += oldhp - hp;
1344 // Update properties on death
1345 if((hp == 0) != (oldhp == 0))
1346 m_properties_sent = false;
1349 u16 PlayerSAO::getBreath() const
1351 return m_player->getBreath();
1354 void PlayerSAO::setBreath(u16 breath)
1356 m_player->setBreath(breath);
1359 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1361 m_armor_groups = armor_groups;
1362 m_armor_groups_sent = false;
1365 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1367 // store these so they can be updated to clients
1368 m_animation_range = frame_range;
1369 m_animation_speed = frame_speed;
1370 m_animation_blend = frame_blend;
1371 m_animation_sent = false;
1374 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1376 // store these so they can be updated to clients
1377 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1378 m_bone_position_sent = false;
1381 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1383 // Attachments need to be handled on both the server and client.
1384 // If we just attach on the server, we can only copy the position of the parent. Attachments
1385 // are still sent to clients at an interval so players might see them lagging, plus we can't
1386 // read and attach to skeletal bones.
1387 // If we just attach on the client, the server still sees the child at its original location.
1388 // This breaks some things so we also give the server the most accurate representation
1389 // even if players only see the client changes.
1391 m_attachment_parent_id = parent_id;
1392 m_attachment_bone = bone;
1393 m_attachment_position = position;
1394 m_attachment_rotation = rotation;
1395 m_attachment_sent = false;
1398 ObjectProperties* PlayerSAO::accessObjectProperties()
1403 void PlayerSAO::notifyObjectPropertiesModified()
1405 m_properties_sent = false;
1408 Inventory* PlayerSAO::getInventory()
1412 const Inventory* PlayerSAO::getInventory() const
1417 InventoryLocation PlayerSAO::getInventoryLocation() const
1419 InventoryLocation loc;
1420 loc.setPlayer(m_player->getName());
1424 void PlayerSAO::setInventoryModified()
1426 m_inventory_not_sent = true;
1429 std::string PlayerSAO::getWieldList() const
1434 int PlayerSAO::getWieldIndex() const
1436 return m_wield_index;
1439 void PlayerSAO::setWieldIndex(int i)
1441 if(i != m_wield_index)
1444 m_wielded_item_not_sent = true;
1448 void PlayerSAO::disconnected()
1452 if(m_player->getPlayerSAO() == this)
1454 m_player->setPlayerSAO(NULL);
1455 m_player->peer_id = 0;
1459 std::string PlayerSAO::getPropertyPacket()
1461 m_prop.is_visible = (true);
1462 return gob_cmd_set_properties(m_prop);
1465 bool PlayerSAO::checkMovementCheat()
1467 bool cheated = false;
1468 if(isAttached() || m_is_singleplayer ||
1469 g_settings->getBool("disable_anticheat"))
1471 m_last_good_position = m_player->getPosition();
1476 Check player movements
1478 NOTE: Actually the server should handle player physics like the
1479 client does and compare player's position to what is calculated
1480 on our side. This is required when eg. players fly due to an
1481 explosion. Altough a node-based alternative might be possible
1482 too, and much more lightweight.
1485 float player_max_speed = 0;
1486 float player_max_speed_up = 0;
1487 if(m_privs.count("fast") != 0){
1489 player_max_speed = m_player->movement_speed_fast;
1490 player_max_speed_up = m_player->movement_speed_fast;
1493 player_max_speed = m_player->movement_speed_walk;
1494 player_max_speed_up = m_player->movement_speed_walk;
1496 // Tolerance. With the lag pool we shouldn't need it.
1497 //player_max_speed *= 2.5;
1498 //player_max_speed_up *= 2.5;
1500 v3f diff = (m_player->getPosition() - m_last_good_position);
1501 float d_vert = diff.Y;
1503 float d_horiz = diff.getLength();
1504 float required_time = d_horiz/player_max_speed;
1505 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1506 required_time = d_vert/player_max_speed;
1507 if(m_move_pool.grab(required_time)){
1508 m_last_good_position = m_player->getPosition();
1510 actionstream<<"Player "<<m_player->getName()
1511 <<" moved too fast; resetting position"
1513 m_player->setPosition(m_last_good_position);
1521 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1522 //update collision box
1523 *toset = m_player->getCollisionbox();
1525 toset->MinEdge += m_base_position;
1526 toset->MaxEdge += m_base_position;
1531 bool PlayerSAO::collideWithObjects(){