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 "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) {
74 // Prototype (registers item for deserialization)
75 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
76 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
77 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
78 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
84 class TestSAO : public ServerActiveObject
87 TestSAO(ServerEnvironment *env, v3f pos):
88 ServerActiveObject(env, pos),
92 ServerActiveObject::registerType(getType(), create);
95 { return ACTIVEOBJECT_TYPE_TEST; }
97 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
98 const std::string &data)
100 return new TestSAO(env, pos);
103 void step(float dtime, bool send_recommended)
112 m_base_position.Y += dtime * BS * 2;
113 if(m_base_position.Y > 8*BS)
114 m_base_position.Y = 2*BS;
116 if(send_recommended == false)
126 data += itos(0); // 0 = position
128 data += itos(m_base_position.X);
130 data += itos(m_base_position.Y);
132 data += itos(m_base_position.Z);
134 ActiveObjectMessage aom(getId(), false, data);
135 m_messages_out.push_back(aom);
139 bool getCollisionBox(aabb3f *toset) {
148 // Prototype (registers item for deserialization)
149 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
154 DEPRECATED: New dropped items are implemented in Lua; see
155 builtin/item_entity.lua.
158 class ItemSAO : public ServerActiveObject
162 { return ACTIVEOBJECT_TYPE_ITEM; }
164 float getMinimumSavedMovement()
167 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
168 const std::string &data)
170 std::istringstream is(data, std::ios::binary);
175 // check if version is supported
178 std::string itemstring = deSerializeString(is);
179 infostream<<"create(): Creating item \""
180 <<itemstring<<"\""<<std::endl;
181 return new ItemSAO(env, pos, itemstring);
184 ItemSAO(ServerEnvironment *env, v3f pos,
185 const std::string itemstring):
186 ServerActiveObject(env, pos),
187 m_itemstring(itemstring),
188 m_itemstring_changed(false),
190 m_last_sent_position(0,0,0)
192 ServerActiveObject::registerType(getType(), create);
195 void step(float dtime, bool send_recommended)
197 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
201 const float interval = 0.2;
202 if(m_move_interval.step(dtime, interval)==false)
206 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
207 collisionMoveResult moveresult;
209 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
210 // Maximum movement without glitches
211 f32 pos_max_d = BS*0.25;
213 if(m_speed_f.getLength()*dtime > pos_max_d)
214 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
215 v3f pos_f = getBasePosition();
216 v3f pos_f_old = pos_f;
217 v3f accel_f = v3f(0,0,0);
219 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
220 pos_max_d, box, stepheight, dtime,
221 pos_f, m_speed_f, accel_f);
223 if(send_recommended == false)
226 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
228 setBasePosition(pos_f);
229 m_last_sent_position = pos_f;
231 std::ostringstream os(std::ios::binary);
232 // command (0 = update position)
235 writeV3F1000(os, m_base_position);
236 // create message and add to list
237 ActiveObjectMessage aom(getId(), false, os.str());
238 m_messages_out.push_back(aom);
240 if(m_itemstring_changed)
242 m_itemstring_changed = false;
244 std::ostringstream os(std::ios::binary);
245 // command (1 = update itemstring)
248 os<<serializeString(m_itemstring);
249 // create message and add to list
250 ActiveObjectMessage aom(getId(), false, os.str());
251 m_messages_out.push_back(aom);
255 std::string getClientInitializationData(u16 protocol_version)
257 std::ostringstream os(std::ios::binary);
261 writeV3F1000(os, m_base_position);
263 os<<serializeString(m_itemstring);
267 std::string getStaticData()
269 infostream<<__FUNCTION_NAME<<std::endl;
270 std::ostringstream os(std::ios::binary);
274 os<<serializeString(m_itemstring);
278 ItemStack createItemStack()
281 IItemDefManager *idef = m_env->getGameDef()->idef();
283 item.deSerialize(m_itemstring, idef);
284 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
285 <<"\" -> item=\""<<item.getItemString()<<"\""
289 catch(SerializationError &e)
291 infostream<<__FUNCTION_NAME<<": serialization error: "
292 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
298 const ToolCapabilities *toolcap,
299 ServerActiveObject *puncher,
300 float time_from_last_punch)
302 // Take item into inventory
303 ItemStack item = createItemStack();
304 Inventory *inv = puncher->getInventory();
307 std::string wieldlist = puncher->getWieldList();
308 ItemStack leftover = inv->addItem(wieldlist, item);
309 puncher->setInventoryModified();
316 m_itemstring = leftover.getItemString();
317 m_itemstring_changed = true;
324 bool getCollisionBox(aabb3f *toset) {
330 std::string m_itemstring;
331 bool m_itemstring_changed;
333 v3f m_last_sent_position;
334 IntervalLimiter m_move_interval;
337 // Prototype (registers item for deserialization)
338 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
340 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
341 const std::string itemstring)
343 return new ItemSAO(env, pos, itemstring);
350 // Prototype (registers item for deserialization)
351 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
353 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
354 const std::string &name, const std::string &state):
355 ServerActiveObject(env, pos),
361 m_acceleration(0,0,0),
363 m_properties_sent(true),
365 m_last_sent_position(0,0,0),
366 m_last_sent_velocity(0,0,0),
367 m_last_sent_position_timer(0),
368 m_last_sent_move_precision(0),
369 m_armor_groups_sent(false),
370 m_animation_speed(0),
371 m_animation_blend(0),
372 m_animation_sent(false),
373 m_bone_position_sent(false),
374 m_attachment_parent_id(0),
375 m_attachment_sent(false)
377 // Only register type if no environment supplied
379 ServerActiveObject::registerType(getType(), create);
383 // Initialize something to armor groups
384 m_armor_groups["fleshy"] = 100;
387 LuaEntitySAO::~LuaEntitySAO()
390 lua_State *L = m_env->getLua();
391 scriptapi_luaentity_rm(L, m_id);
395 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
397 ServerActiveObject::addedToEnvironment(dtime_s);
399 // Create entity from name
400 lua_State *L = m_env->getLua();
401 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
405 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
406 // Initialize HP from properties
407 m_hp = m_prop.hp_max;
408 // Activate entity, supplying serialized state
409 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
413 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
414 const std::string &data)
422 std::istringstream is(data, std::ios::binary);
424 u8 version = readU8(is);
425 // check if version is supported
427 name = deSerializeString(is);
428 state = deSerializeLongString(is);
430 else if(version == 1){
431 name = deSerializeString(is);
432 state = deSerializeLongString(is);
434 velocity = readV3F1000(is);
439 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
440 <<state<<"\")"<<std::endl;
441 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
443 sao->m_velocity = velocity;
448 bool LuaEntitySAO::isAttached()
450 if(!m_attachment_parent_id)
452 // Check if the parent still exists
453 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
459 void LuaEntitySAO::step(float dtime, bool send_recommended)
461 if(!m_properties_sent)
463 m_properties_sent = true;
464 std::string str = getPropertyPacket();
465 // create message and add to list
466 ActiveObjectMessage aom(getId(), true, str);
467 m_messages_out.push_back(aom);
470 // If attached, check that our parent is still there. If it isn't, detach.
471 if(m_attachment_parent_id && !isAttached())
473 m_attachment_parent_id = 0;
474 m_attachment_bone = "";
475 m_attachment_position = v3f(0,0,0);
476 m_attachment_rotation = v3f(0,0,0);
477 sendPosition(false, true);
480 m_last_sent_position_timer += dtime;
482 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
483 // If the object gets detached this comes into effect automatically from the last known origin
486 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
487 m_base_position = pos;
488 m_velocity = v3f(0,0,0);
489 m_acceleration = v3f(0,0,0);
494 core::aabbox3d<f32> box = m_prop.collisionbox;
497 collisionMoveResult moveresult;
498 f32 pos_max_d = BS*0.25; // Distance per iteration
499 f32 stepheight = 0; // Maximum climbable step height
500 v3f p_pos = m_base_position;
501 v3f p_velocity = m_velocity;
502 v3f p_acceleration = m_acceleration;
503 moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
504 pos_max_d, box, stepheight, dtime,
505 p_pos, p_velocity, p_acceleration);
507 m_base_position = p_pos;
508 m_velocity = p_velocity;
509 m_acceleration = p_acceleration;
511 m_base_position += dtime * m_velocity + 0.5 * dtime
512 * dtime * m_acceleration;
513 m_velocity += dtime * m_acceleration;
518 lua_State *L = m_env->getLua();
519 scriptapi_luaentity_step(L, m_id, dtime);
522 if(send_recommended == false)
527 // TODO: force send when acceleration changes enough?
528 float minchange = 0.2*BS;
529 if(m_last_sent_position_timer > 1.0){
531 } else if(m_last_sent_position_timer > 0.2){
534 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
535 move_d += m_last_sent_move_precision;
536 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
537 if(move_d > minchange || vel_d > minchange ||
538 fabs(m_yaw - m_last_sent_yaw) > 1.0){
539 sendPosition(true, false);
543 if(m_armor_groups_sent == false){
544 m_armor_groups_sent = true;
545 std::string str = gob_cmd_update_armor_groups(
547 // create message and add to list
548 ActiveObjectMessage aom(getId(), true, str);
549 m_messages_out.push_back(aom);
552 if(m_animation_sent == false){
553 m_animation_sent = true;
554 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
555 // create message and add to list
556 ActiveObjectMessage aom(getId(), true, str);
557 m_messages_out.push_back(aom);
560 if(m_bone_position_sent == false){
561 m_bone_position_sent = true;
562 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
563 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
564 // create message and add to list
565 ActiveObjectMessage aom(getId(), true, str);
566 m_messages_out.push_back(aom);
570 if(m_attachment_sent == false){
571 m_attachment_sent = true;
572 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
573 // create message and add to list
574 ActiveObjectMessage aom(getId(), true, str);
575 m_messages_out.push_back(aom);
579 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
581 std::ostringstream os(std::ios::binary);
583 if(protocol_version >= 14)
585 writeU8(os, 1); // version
586 os<<serializeString(""); // name
587 writeU8(os, 0); // is_player
588 writeS16(os, getId()); //id
589 writeV3F1000(os, m_base_position);
590 writeF1000(os, m_yaw);
593 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
594 os<<serializeLongString(getPropertyPacket()); // message 1
595 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
596 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
597 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
598 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
600 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
604 writeU8(os, 0); // version
605 os<<serializeString(""); // name
606 writeU8(os, 0); // is_player
607 writeV3F1000(os, m_base_position);
608 writeF1000(os, m_yaw);
610 writeU8(os, 2); // number of messages stuffed in here
611 os<<serializeLongString(getPropertyPacket()); // message 1
612 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
619 std::string LuaEntitySAO::getStaticData()
621 verbosestream<<__FUNCTION_NAME<<std::endl;
622 std::ostringstream os(std::ios::binary);
626 os<<serializeString(m_init_name);
629 lua_State *L = m_env->getLua();
630 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
631 os<<serializeLongString(state);
633 os<<serializeLongString(m_init_state);
638 writeV3F1000(os, m_velocity);
640 writeF1000(os, m_yaw);
644 int LuaEntitySAO::punch(v3f dir,
645 const ToolCapabilities *toolcap,
646 ServerActiveObject *puncher,
647 float time_from_last_punch)
650 // Delete unknown LuaEntities when punched
655 // It's best that attachments cannot be punched
659 ItemStack *punchitem = NULL;
660 ItemStack punchitem_static;
662 punchitem_static = puncher->getWieldedItem();
663 punchitem = &punchitem_static;
666 PunchDamageResult result = getPunchDamage(
670 time_from_last_punch);
674 setHP(getHP() - result.damage);
676 actionstream<<getDescription()<<" punched by "
677 <<puncher->getDescription()<<", damage "<<result.damage
678 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
681 std::string str = gob_cmd_punched(result.damage, getHP());
682 // create message and add to list
683 ActiveObjectMessage aom(getId(), true, str);
684 m_messages_out.push_back(aom);
691 lua_State *L = m_env->getLua();
692 scriptapi_luaentity_punch(L, m_id, puncher,
693 time_from_last_punch, toolcap, dir);
698 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
702 // It's best that attachments cannot be clicked
705 lua_State *L = m_env->getLua();
706 scriptapi_luaentity_rightclick(L, m_id, clicker);
709 void LuaEntitySAO::setPos(v3f pos)
713 m_base_position = pos;
714 sendPosition(false, true);
717 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
721 m_base_position = pos;
723 sendPosition(true, true);
726 float LuaEntitySAO::getMinimumSavedMovement()
731 std::string LuaEntitySAO::getDescription()
733 std::ostringstream os(std::ios::binary);
734 os<<"LuaEntitySAO at (";
735 os<<(m_base_position.X/BS)<<",";
736 os<<(m_base_position.Y/BS)<<",";
737 os<<(m_base_position.Z/BS);
742 void LuaEntitySAO::setHP(s16 hp)
748 s16 LuaEntitySAO::getHP() const
753 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
755 m_armor_groups = armor_groups;
756 m_armor_groups_sent = false;
759 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
761 m_animation_range = frame_range;
762 m_animation_speed = frame_speed;
763 m_animation_blend = frame_blend;
764 m_animation_sent = false;
767 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
769 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
770 m_bone_position_sent = false;
773 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
775 // Attachments need to be handled on both the server and client.
776 // If we just attach on the server, we can only copy the position of the parent. Attachments
777 // are still sent to clients at an interval so players might see them lagging, plus we can't
778 // read and attach to skeletal bones.
779 // If we just attach on the client, the server still sees the child at its original location.
780 // This breaks some things so we also give the server the most accurate representation
781 // even if players only see the client changes.
783 m_attachment_parent_id = parent_id;
784 m_attachment_bone = bone;
785 m_attachment_position = position;
786 m_attachment_rotation = rotation;
787 m_attachment_sent = false;
790 ObjectProperties* LuaEntitySAO::accessObjectProperties()
795 void LuaEntitySAO::notifyObjectPropertiesModified()
797 m_properties_sent = false;
800 void LuaEntitySAO::setVelocity(v3f velocity)
802 m_velocity = velocity;
805 v3f LuaEntitySAO::getVelocity()
810 void LuaEntitySAO::setAcceleration(v3f acceleration)
812 m_acceleration = acceleration;
815 v3f LuaEntitySAO::getAcceleration()
817 return m_acceleration;
820 void LuaEntitySAO::setYaw(float yaw)
825 float LuaEntitySAO::getYaw()
830 void LuaEntitySAO::setTextureMod(const std::string &mod)
832 std::string str = gob_cmd_set_texture_mod(mod);
833 // create message and add to list
834 ActiveObjectMessage aom(getId(), true, str);
835 m_messages_out.push_back(aom);
838 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
839 bool select_horiz_by_yawpitch)
841 std::string str = gob_cmd_set_sprite(
845 select_horiz_by_yawpitch
847 // create message and add to list
848 ActiveObjectMessage aom(getId(), true, str);
849 m_messages_out.push_back(aom);
852 std::string LuaEntitySAO::getName()
857 std::string LuaEntitySAO::getPropertyPacket()
859 return gob_cmd_set_properties(m_prop);
862 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
864 // If the object is attached client-side, don't waste bandwidth sending its position to clients
868 m_last_sent_move_precision = m_base_position.getDistanceFrom(
869 m_last_sent_position);
870 m_last_sent_position_timer = 0;
871 m_last_sent_yaw = m_yaw;
872 m_last_sent_position = m_base_position;
873 m_last_sent_velocity = m_velocity;
874 //m_last_sent_acceleration = m_acceleration;
876 float update_interval = m_env->getSendRecommendedInterval();
878 std::string str = gob_cmd_update_position(
887 // create message and add to list
888 ActiveObjectMessage aom(getId(), false, str);
889 m_messages_out.push_back(aom);
892 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
895 //update collision box
896 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
897 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
899 toset->MinEdge += m_base_position;
900 toset->MaxEdge += m_base_position;
912 // No prototype, PlayerSAO does not need to be deserialized
914 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
915 const std::set<std::string> &privs, bool is_singleplayer):
916 ServerActiveObject(env_, v3f(0,0,0)),
920 m_last_good_position(0,0,0),
921 m_last_good_position_age(0),
922 m_time_from_last_punch(0),
923 m_nocheat_dig_pos(32767, 32767, 32767),
924 m_nocheat_dig_time(0),
926 m_position_not_sent(false),
927 m_armor_groups_sent(false),
928 m_properties_sent(true),
930 m_is_singleplayer(is_singleplayer),
931 m_animation_sent(false),
932 m_bone_position_sent(false),
933 m_attachment_sent(false),
936 m_inventory_not_sent(false),
937 m_hp_not_sent(false),
938 m_wielded_item_not_sent(false)
941 assert(m_peer_id != 0);
942 setBasePosition(m_player->getPosition());
943 m_inventory = &m_player->inventory;
944 m_armor_groups["fleshy"] = 100;
946 m_prop.hp_max = PLAYER_MAX_HP;
947 m_prop.physical = false;
949 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
950 // start of default appearance, this should be overwritten by LUA
951 m_prop.visual = "upright_sprite";
952 m_prop.visual_size = v2f(1, 2);
953 m_prop.textures.clear();
954 m_prop.textures.push_back("player.png");
955 m_prop.textures.push_back("player_back.png");
956 m_prop.colors.clear();
957 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
958 m_prop.spritediv = v2s16(1,1);
959 // end of default appearance
960 m_prop.is_visible = true;
961 m_prop.makes_footstep_sound = true;
964 PlayerSAO::~PlayerSAO()
966 if(m_inventory != &m_player->inventory)
971 std::string PlayerSAO::getDescription()
973 return std::string("player ") + m_player->getName();
976 // Called after id has been set and has been inserted in environment
977 void PlayerSAO::addedToEnvironment(u32 dtime_s)
979 ServerActiveObject::addedToEnvironment(dtime_s);
980 ServerActiveObject::setBasePosition(m_player->getPosition());
981 m_player->setPlayerSAO(this);
982 m_player->peer_id = m_peer_id;
983 m_last_good_position = m_player->getPosition();
984 m_last_good_position_age = 0.0;
987 // Called before removing from environment
988 void PlayerSAO::removingFromEnvironment()
990 ServerActiveObject::removingFromEnvironment();
991 if(m_player->getPlayerSAO() == this)
993 m_player->setPlayerSAO(NULL);
994 m_player->peer_id = 0;
998 bool PlayerSAO::isStaticAllowed() const
1003 bool PlayerSAO::unlimitedTransferDistance() const
1005 return g_settings->getBool("unlimited_player_transfer_distance");
1008 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
1010 std::ostringstream os(std::ios::binary);
1012 if(protocol_version >= 15)
1014 writeU8(os, 1); // version
1015 os<<serializeString(m_player->getName()); // name
1016 writeU8(os, 1); // is_player
1017 writeS16(os, getId()); //id
1018 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1019 writeF1000(os, m_player->getYaw());
1020 writeS16(os, getHP());
1022 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
1023 os<<serializeLongString(getPropertyPacket()); // message 1
1024 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1025 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1026 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1027 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1029 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1033 writeU8(os, 0); // version
1034 os<<serializeString(m_player->getName()); // name
1035 writeU8(os, 1); // is_player
1036 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1037 writeF1000(os, m_player->getYaw());
1038 writeS16(os, getHP());
1039 writeU8(os, 2); // number of messages stuffed in here
1040 os<<serializeLongString(getPropertyPacket()); // message 1
1041 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1048 std::string PlayerSAO::getStaticData()
1054 bool PlayerSAO::isAttached()
1056 if(!m_attachment_parent_id)
1058 // Check if the parent still exists
1059 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1065 void PlayerSAO::step(float dtime, bool send_recommended)
1067 if(!m_properties_sent)
1069 m_properties_sent = true;
1070 std::string str = getPropertyPacket();
1071 // create message and add to list
1072 ActiveObjectMessage aom(getId(), true, str);
1073 m_messages_out.push_back(aom);
1076 // If attached, check that our parent is still there. If it isn't, detach.
1077 if(m_attachment_parent_id && !isAttached())
1079 m_attachment_parent_id = 0;
1080 m_attachment_bone = "";
1081 m_attachment_position = v3f(0,0,0);
1082 m_attachment_rotation = v3f(0,0,0);
1083 m_player->setPosition(m_last_good_position);
1087 m_time_from_last_punch += dtime;
1088 m_nocheat_dig_time += dtime;
1090 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1091 // If the object gets detached this comes into effect automatically from the last known origin
1094 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1095 m_last_good_position = pos;
1096 m_last_good_position_age = 0;
1097 m_player->setPosition(pos);
1101 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1103 m_last_good_position = m_player->getPosition();
1104 m_last_good_position_age = 0;
1109 Check player movements
1111 NOTE: Actually the server should handle player physics like the
1112 client does and compare player's position to what is calculated
1113 on our side. This is required when eg. players fly due to an
1114 explosion. Altough a node-based alternative might be possible
1115 too, and much more lightweight.
1118 float player_max_speed = 0;
1119 float player_max_speed_up = 0;
1120 if(m_privs.count("fast") != 0){
1122 player_max_speed = BS * 20;
1123 player_max_speed_up = BS * 20;
1126 player_max_speed = BS * 4.0;
1127 player_max_speed_up = BS * 4.0;
1130 player_max_speed *= 2.5;
1131 player_max_speed_up *= 2.5;
1133 m_last_good_position_age += dtime;
1134 if(m_last_good_position_age >= 1.0){
1135 float age = m_last_good_position_age;
1136 v3f diff = (m_player->getPosition() - m_last_good_position);
1137 float d_vert = diff.Y;
1139 float d_horiz = diff.getLength();
1140 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1141 <<(d_horiz/age)<<std::endl;*/
1142 if(d_horiz <= age * player_max_speed &&
1143 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1144 m_last_good_position = m_player->getPosition();
1146 actionstream<<"Player "<<m_player->getName()
1147 <<" moved too fast; resetting position"
1149 m_player->setPosition(m_last_good_position);
1152 m_last_good_position_age = 0;
1157 if(send_recommended == false)
1160 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1161 if(m_position_not_sent && !isAttached())
1163 m_position_not_sent = false;
1164 float update_interval = m_env->getSendRecommendedInterval();
1166 if(isAttached()) // Just in case we ever do send attachment position too
1167 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1169 pos = m_player->getPosition() + v3f(0,BS*1,0);
1170 std::string str = gob_cmd_update_position(
1179 // create message and add to list
1180 ActiveObjectMessage aom(getId(), false, str);
1181 m_messages_out.push_back(aom);
1184 if(m_wielded_item_not_sent)
1186 m_wielded_item_not_sent = false;
1187 // GenericCAO has no special way to show this
1190 if(m_armor_groups_sent == false){
1191 m_armor_groups_sent = true;
1192 std::string str = gob_cmd_update_armor_groups(
1194 // create message and add to list
1195 ActiveObjectMessage aom(getId(), true, str);
1196 m_messages_out.push_back(aom);
1199 if(m_animation_sent == false){
1200 m_animation_sent = true;
1201 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1202 // create message and add to list
1203 ActiveObjectMessage aom(getId(), true, str);
1204 m_messages_out.push_back(aom);
1207 if(m_bone_position_sent == false){
1208 m_bone_position_sent = true;
1209 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1210 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1211 // create message and add to list
1212 ActiveObjectMessage aom(getId(), true, str);
1213 m_messages_out.push_back(aom);
1217 if(m_attachment_sent == false){
1218 m_attachment_sent = true;
1219 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1220 // create message and add to list
1221 ActiveObjectMessage aom(getId(), true, str);
1222 m_messages_out.push_back(aom);
1226 void PlayerSAO::setBasePosition(const v3f &position)
1228 // This needs to be ran for attachments too
1229 ServerActiveObject::setBasePosition(position);
1230 m_position_not_sent = true;
1233 void PlayerSAO::setPos(v3f pos)
1237 m_player->setPosition(pos);
1238 // Movement caused by this command is always valid
1239 m_last_good_position = pos;
1240 m_last_good_position_age = 0;
1241 // Force position change on client
1245 void PlayerSAO::moveTo(v3f pos, bool continuous)
1249 m_player->setPosition(pos);
1250 // Movement caused by this command is always valid
1251 m_last_good_position = pos;
1252 m_last_good_position_age = 0;
1253 // Force position change on client
1257 void PlayerSAO::setYaw(float yaw)
1259 m_player->setYaw(yaw);
1260 // Force change on client
1264 void PlayerSAO::setPitch(float pitch)
1266 m_player->setPitch(pitch);
1267 // Force change on client
1271 int PlayerSAO::punch(v3f dir,
1272 const ToolCapabilities *toolcap,
1273 ServerActiveObject *puncher,
1274 float time_from_last_punch)
1276 // It's best that attachments cannot be punched
1283 // No effect if PvP disabled
1284 if(g_settings->getBool("enable_pvp") == false){
1285 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1286 std::string str = gob_cmd_punched(0, getHP());
1287 // create message and add to list
1288 ActiveObjectMessage aom(getId(), true, str);
1289 m_messages_out.push_back(aom);
1294 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1295 time_from_last_punch);
1297 actionstream<<"Player "<<m_player->getName()<<" punched by "
1298 <<puncher->getDescription()<<", damage "<<hitparams.hp
1301 setHP(getHP() - hitparams.hp);
1303 if(hitparams.hp != 0)
1305 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1306 // create message and add to list
1307 ActiveObjectMessage aom(getId(), true, str);
1308 m_messages_out.push_back(aom);
1311 return hitparams.wear;
1314 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1318 s16 PlayerSAO::getHP() const
1320 return m_player->hp;
1323 void PlayerSAO::setHP(s16 hp)
1325 s16 oldhp = m_player->hp;
1329 else if(hp > PLAYER_MAX_HP)
1332 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1334 m_hp_not_sent = true; // fix wrong prediction on client
1341 m_hp_not_sent = true;
1343 // On death or reincarnation send an active object message
1344 if((hp == 0) != (oldhp == 0))
1346 // Will send new is_visible value based on (getHP()!=0)
1347 m_properties_sent = false;
1349 std::string str = gob_cmd_punched(0, getHP());
1350 ActiveObjectMessage aom(getId(), true, str);
1351 m_messages_out.push_back(aom);
1355 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1357 m_armor_groups = armor_groups;
1358 m_armor_groups_sent = false;
1361 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1363 // store these so they can be updated to clients
1364 m_animation_range = frame_range;
1365 m_animation_speed = frame_speed;
1366 m_animation_blend = frame_blend;
1367 m_animation_sent = false;
1370 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1372 // store these so they can be updated to clients
1373 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1374 m_bone_position_sent = false;
1377 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1379 // Attachments need to be handled on both the server and client.
1380 // If we just attach on the server, we can only copy the position of the parent. Attachments
1381 // are still sent to clients at an interval so players might see them lagging, plus we can't
1382 // read and attach to skeletal bones.
1383 // If we just attach on the client, the server still sees the child at its original location.
1384 // This breaks some things so we also give the server the most accurate representation
1385 // even if players only see the client changes.
1387 m_attachment_parent_id = parent_id;
1388 m_attachment_bone = bone;
1389 m_attachment_position = position;
1390 m_attachment_rotation = rotation;
1391 m_attachment_sent = false;
1394 ObjectProperties* PlayerSAO::accessObjectProperties()
1399 void PlayerSAO::notifyObjectPropertiesModified()
1401 m_properties_sent = false;
1404 Inventory* PlayerSAO::getInventory()
1408 const Inventory* PlayerSAO::getInventory() const
1413 InventoryLocation PlayerSAO::getInventoryLocation() const
1415 InventoryLocation loc;
1416 loc.setPlayer(m_player->getName());
1420 void PlayerSAO::setInventoryModified()
1422 m_inventory_not_sent = true;
1425 std::string PlayerSAO::getWieldList() const
1430 int PlayerSAO::getWieldIndex() const
1432 return m_wield_index;
1435 void PlayerSAO::setWieldIndex(int i)
1437 if(i != m_wield_index)
1440 m_wielded_item_not_sent = true;
1444 void PlayerSAO::disconnected()
1448 if(m_player->getPlayerSAO() == this)
1450 m_player->setPlayerSAO(NULL);
1451 m_player->peer_id = 0;
1455 std::string PlayerSAO::getPropertyPacket()
1457 m_prop.is_visible = (true);
1458 return gob_cmd_set_properties(m_prop);
1461 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1462 //player collision handling is already done clientside no need to do it twice