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)),
945 m_last_good_position(0,0,0),
946 m_time_from_last_punch(0),
947 m_nocheat_dig_pos(32767, 32767, 32767),
948 m_nocheat_dig_time(0),
950 m_position_not_sent(false),
951 m_armor_groups_sent(false),
952 m_properties_sent(true),
954 m_is_singleplayer(is_singleplayer),
955 m_animation_speed(0),
956 m_animation_blend(0),
957 m_animation_sent(false),
958 m_bone_position_sent(false),
959 m_attachment_parent_id(0),
960 m_attachment_sent(false),
963 m_inventory_not_sent(false),
964 m_hp_not_sent(false),
965 m_breath_not_sent(false),
966 m_wielded_item_not_sent(false),
967 m_physics_override_speed(1),
968 m_physics_override_jump(1),
969 m_physics_override_gravity(1),
970 m_physics_override_sent(false)
973 assert(m_peer_id != 0);
974 setBasePosition(m_player->getPosition());
975 m_inventory = &m_player->inventory;
976 m_armor_groups["fleshy"] = 100;
978 m_prop.hp_max = PLAYER_MAX_HP;
979 m_prop.physical = false;
981 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
982 // start of default appearance, this should be overwritten by LUA
983 m_prop.visual = "upright_sprite";
984 m_prop.visual_size = v2f(1, 2);
985 m_prop.textures.clear();
986 m_prop.textures.push_back("player.png");
987 m_prop.textures.push_back("player_back.png");
988 m_prop.colors.clear();
989 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
990 m_prop.spritediv = v2s16(1,1);
991 // end of default appearance
992 m_prop.is_visible = true;
993 m_prop.makes_footstep_sound = true;
996 PlayerSAO::~PlayerSAO()
998 if(m_inventory != &m_player->inventory)
1003 std::string PlayerSAO::getDescription()
1005 return std::string("player ") + m_player->getName();
1008 // Called after id has been set and has been inserted in environment
1009 void PlayerSAO::addedToEnvironment(u32 dtime_s)
1011 ServerActiveObject::addedToEnvironment(dtime_s);
1012 ServerActiveObject::setBasePosition(m_player->getPosition());
1013 m_player->setPlayerSAO(this);
1014 m_player->peer_id = m_peer_id;
1015 m_last_good_position = m_player->getPosition();
1018 // Called before removing from environment
1019 void PlayerSAO::removingFromEnvironment()
1021 ServerActiveObject::removingFromEnvironment();
1022 if(m_player->getPlayerSAO() == this)
1024 m_player->setPlayerSAO(NULL);
1025 m_player->peer_id = 0;
1029 bool PlayerSAO::isStaticAllowed() const
1034 bool PlayerSAO::unlimitedTransferDistance() const
1036 return g_settings->getBool("unlimited_player_transfer_distance");
1039 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1041 std::ostringstream os(std::ios::binary);
1043 if(protocol_version >= 15)
1045 writeU8(os, 1); // version
1046 os<<serializeString(m_player->getName()); // name
1047 writeU8(os, 1); // is_player
1048 writeS16(os, getId()); //id
1049 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1050 writeF1000(os, m_player->getYaw());
1051 writeS16(os, getHP());
1053 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
1054 os<<serializeLongString(getPropertyPacket()); // message 1
1055 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1056 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1057 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1058 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1060 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1061 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
1065 writeU8(os, 0); // version
1066 os<<serializeString(m_player->getName()); // name
1067 writeU8(os, 1); // is_player
1068 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1069 writeF1000(os, m_player->getYaw());
1070 writeS16(os, getHP());
1071 writeU8(os, 2); // number of messages stuffed in here
1072 os<<serializeLongString(getPropertyPacket()); // message 1
1073 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1080 std::string PlayerSAO::getStaticData()
1086 bool PlayerSAO::isAttached()
1088 if(!m_attachment_parent_id)
1090 // Check if the parent still exists
1091 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1097 void PlayerSAO::step(float dtime, bool send_recommended)
1099 if(!m_properties_sent)
1101 m_properties_sent = true;
1102 std::string str = getPropertyPacket();
1103 // create message and add to list
1104 ActiveObjectMessage aom(getId(), true, str);
1105 m_messages_out.push_back(aom);
1108 // If attached, check that our parent is still there. If it isn't, detach.
1109 if(m_attachment_parent_id && !isAttached())
1111 m_attachment_parent_id = 0;
1112 m_attachment_bone = "";
1113 m_attachment_position = v3f(0,0,0);
1114 m_attachment_rotation = v3f(0,0,0);
1115 m_player->setPosition(m_last_good_position);
1119 //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
1121 // Set lag pool maximums based on estimated lag
1122 const float LAG_POOL_MIN = 5.0;
1123 float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
1124 if(lag_pool_max < LAG_POOL_MIN)
1125 lag_pool_max = LAG_POOL_MIN;
1126 m_dig_pool.setMax(lag_pool_max);
1127 m_move_pool.setMax(lag_pool_max);
1129 // Increment cheat prevention timers
1130 m_dig_pool.add(dtime);
1131 m_move_pool.add(dtime);
1132 m_time_from_last_punch += dtime;
1133 m_nocheat_dig_time += dtime;
1135 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1136 // If the object gets detached this comes into effect automatically from the last known origin
1139 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1140 m_last_good_position = pos;
1141 m_player->setPosition(pos);
1144 if(send_recommended == false)
1147 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1148 if(m_position_not_sent && !isAttached())
1150 m_position_not_sent = false;
1151 float update_interval = m_env->getSendRecommendedInterval();
1153 if(isAttached()) // Just in case we ever do send attachment position too
1154 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1156 pos = m_player->getPosition() + v3f(0,BS*1,0);
1157 std::string str = gob_cmd_update_position(
1166 // create message and add to list
1167 ActiveObjectMessage aom(getId(), false, str);
1168 m_messages_out.push_back(aom);
1171 if(m_wielded_item_not_sent)
1173 m_wielded_item_not_sent = false;
1174 // GenericCAO has no special way to show this
1177 if(m_armor_groups_sent == false){
1178 m_armor_groups_sent = true;
1179 std::string str = gob_cmd_update_armor_groups(
1181 // create message and add to list
1182 ActiveObjectMessage aom(getId(), true, str);
1183 m_messages_out.push_back(aom);
1186 if(m_physics_override_sent == false){
1187 m_physics_override_sent = true;
1188 std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
1189 // create message and add to list
1190 ActiveObjectMessage aom(getId(), true, str);
1191 m_messages_out.push_back(aom);
1194 if(m_animation_sent == false){
1195 m_animation_sent = true;
1196 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1197 // create message and add to list
1198 ActiveObjectMessage aom(getId(), true, str);
1199 m_messages_out.push_back(aom);
1202 if(m_bone_position_sent == false){
1203 m_bone_position_sent = true;
1204 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1205 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1206 // create message and add to list
1207 ActiveObjectMessage aom(getId(), true, str);
1208 m_messages_out.push_back(aom);
1212 if(m_attachment_sent == false){
1213 m_attachment_sent = true;
1214 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1215 // create message and add to list
1216 ActiveObjectMessage aom(getId(), true, str);
1217 m_messages_out.push_back(aom);
1221 void PlayerSAO::setBasePosition(const v3f &position)
1223 // This needs to be ran for attachments too
1224 ServerActiveObject::setBasePosition(position);
1225 m_position_not_sent = true;
1228 void PlayerSAO::setPos(v3f pos)
1232 m_player->setPosition(pos);
1233 // Movement caused by this command is always valid
1234 m_last_good_position = pos;
1235 // Force position change on client
1239 void PlayerSAO::moveTo(v3f pos, bool continuous)
1243 m_player->setPosition(pos);
1244 // Movement caused by this command is always valid
1245 m_last_good_position = pos;
1246 // Force position change on client
1250 void PlayerSAO::setYaw(float yaw)
1252 m_player->setYaw(yaw);
1253 // Force change on client
1257 void PlayerSAO::setPitch(float pitch)
1259 m_player->setPitch(pitch);
1260 // Force change on client
1264 int PlayerSAO::punch(v3f dir,
1265 const ToolCapabilities *toolcap,
1266 ServerActiveObject *puncher,
1267 float time_from_last_punch)
1269 // It's best that attachments cannot be punched
1276 // No effect if PvP disabled
1277 if(g_settings->getBool("enable_pvp") == false){
1278 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1279 std::string str = gob_cmd_punched(0, getHP());
1280 // create message and add to list
1281 ActiveObjectMessage aom(getId(), true, str);
1282 m_messages_out.push_back(aom);
1287 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1288 time_from_last_punch);
1290 std::string punchername = "nil";
1293 punchername = puncher->getDescription();
1295 actionstream<<"Player "<<m_player->getName()<<" punched by "
1296 <<punchername<<", damage "<<hitparams.hp
1299 setHP(getHP() - hitparams.hp);
1301 if(hitparams.hp != 0)
1303 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1304 // create message and add to list
1305 ActiveObjectMessage aom(getId(), true, str);
1306 m_messages_out.push_back(aom);
1309 return hitparams.wear;
1312 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1316 s16 PlayerSAO::getHP() const
1318 return m_player->hp;
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 // On death or reincarnation send an active object message
1342 if((hp == 0) != (oldhp == 0))
1344 // Will send new is_visible value based on (getHP()!=0)
1345 m_properties_sent = false;
1347 std::string str = gob_cmd_punched(0, getHP());
1348 ActiveObjectMessage aom(getId(), true, str);
1349 m_messages_out.push_back(aom);
1353 u16 PlayerSAO::getBreath() const
1355 return m_player->getBreath();
1358 void PlayerSAO::setBreath(u16 breath)
1360 m_player->setBreath(breath);
1363 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1365 m_armor_groups = armor_groups;
1366 m_armor_groups_sent = false;
1369 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1371 // store these so they can be updated to clients
1372 m_animation_range = frame_range;
1373 m_animation_speed = frame_speed;
1374 m_animation_blend = frame_blend;
1375 m_animation_sent = false;
1378 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1380 // store these so they can be updated to clients
1381 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1382 m_bone_position_sent = false;
1385 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1387 // Attachments need to be handled on both the server and client.
1388 // If we just attach on the server, we can only copy the position of the parent. Attachments
1389 // are still sent to clients at an interval so players might see them lagging, plus we can't
1390 // read and attach to skeletal bones.
1391 // If we just attach on the client, the server still sees the child at its original location.
1392 // This breaks some things so we also give the server the most accurate representation
1393 // even if players only see the client changes.
1395 m_attachment_parent_id = parent_id;
1396 m_attachment_bone = bone;
1397 m_attachment_position = position;
1398 m_attachment_rotation = rotation;
1399 m_attachment_sent = false;
1402 ObjectProperties* PlayerSAO::accessObjectProperties()
1407 void PlayerSAO::notifyObjectPropertiesModified()
1409 m_properties_sent = false;
1412 Inventory* PlayerSAO::getInventory()
1416 const Inventory* PlayerSAO::getInventory() const
1421 InventoryLocation PlayerSAO::getInventoryLocation() const
1423 InventoryLocation loc;
1424 loc.setPlayer(m_player->getName());
1428 void PlayerSAO::setInventoryModified()
1430 m_inventory_not_sent = true;
1433 std::string PlayerSAO::getWieldList() const
1438 int PlayerSAO::getWieldIndex() const
1440 return m_wield_index;
1443 void PlayerSAO::setWieldIndex(int i)
1445 if(i != m_wield_index)
1448 m_wielded_item_not_sent = true;
1452 void PlayerSAO::disconnected()
1456 if(m_player->getPlayerSAO() == this)
1458 m_player->setPlayerSAO(NULL);
1459 m_player->peer_id = 0;
1463 std::string PlayerSAO::getPropertyPacket()
1465 m_prop.is_visible = (true);
1466 return gob_cmd_set_properties(m_prop);
1469 bool PlayerSAO::checkMovementCheat()
1471 bool cheated = false;
1472 if(isAttached() || m_is_singleplayer ||
1473 g_settings->getBool("disable_anticheat"))
1475 m_last_good_position = m_player->getPosition();
1480 Check player movements
1482 NOTE: Actually the server should handle player physics like the
1483 client does and compare player's position to what is calculated
1484 on our side. This is required when eg. players fly due to an
1485 explosion. Altough a node-based alternative might be possible
1486 too, and much more lightweight.
1489 float player_max_speed = 0;
1490 float player_max_speed_up = 0;
1491 if(m_privs.count("fast") != 0){
1493 player_max_speed = m_player->movement_speed_fast;
1494 player_max_speed_up = m_player->movement_speed_fast;
1497 player_max_speed = m_player->movement_speed_walk;
1498 player_max_speed_up = m_player->movement_speed_walk;
1500 // Tolerance. With the lag pool we shouldn't need it.
1501 //player_max_speed *= 2.5;
1502 //player_max_speed_up *= 2.5;
1504 v3f diff = (m_player->getPosition() - m_last_good_position);
1505 float d_vert = diff.Y;
1507 float d_horiz = diff.getLength();
1508 float required_time = d_horiz/player_max_speed;
1509 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1510 required_time = d_vert/player_max_speed;
1511 if(m_move_pool.grab(required_time)){
1512 m_last_good_position = m_player->getPosition();
1514 actionstream<<"Player "<<m_player->getName()
1515 <<" moved too fast; resetting position"
1517 m_player->setPosition(m_last_good_position);
1525 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1526 //update collision box
1527 *toset = m_player->getCollisionbox();
1529 toset->MinEdge += m_base_position;
1530 toset->MaxEdge += m_base_position;
1535 bool PlayerSAO::collideWithObjects(){