3 Copyright (C) 2010-2011 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 core::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;
70 // Prototype (registers item for deserialization)
71 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
72 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
73 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
74 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
80 class TestSAO : public ServerActiveObject
83 TestSAO(ServerEnvironment *env, v3f pos):
84 ServerActiveObject(env, pos),
88 ServerActiveObject::registerType(getType(), create);
91 { return ACTIVEOBJECT_TYPE_TEST; }
93 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
94 const std::string &data)
96 return new TestSAO(env, pos);
99 void step(float dtime, bool send_recommended)
108 m_base_position.Y += dtime * BS * 2;
109 if(m_base_position.Y > 8*BS)
110 m_base_position.Y = 2*BS;
112 if(send_recommended == false)
122 data += itos(0); // 0 = position
124 data += itos(m_base_position.X);
126 data += itos(m_base_position.Y);
128 data += itos(m_base_position.Z);
130 ActiveObjectMessage aom(getId(), false, data);
131 m_messages_out.push_back(aom);
140 // Prototype (registers item for deserialization)
141 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
146 DEPRECATED: New dropped items are implemented in Lua; see
147 builtin/item_entity.lua.
150 class ItemSAO : public ServerActiveObject
154 { return ACTIVEOBJECT_TYPE_ITEM; }
156 float getMinimumSavedMovement()
159 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
160 const std::string &data)
162 std::istringstream is(data, std::ios::binary);
167 // check if version is supported
170 std::string itemstring = deSerializeString(is);
171 infostream<<"create(): Creating item \""
172 <<itemstring<<"\""<<std::endl;
173 return new ItemSAO(env, pos, itemstring);
176 ItemSAO(ServerEnvironment *env, v3f pos,
177 const std::string itemstring):
178 ServerActiveObject(env, pos),
179 m_itemstring(itemstring),
180 m_itemstring_changed(false),
182 m_last_sent_position(0,0,0)
184 ServerActiveObject::registerType(getType(), create);
187 void step(float dtime, bool send_recommended)
189 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
193 const float interval = 0.2;
194 if(m_move_interval.step(dtime, interval)==false)
198 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
199 collisionMoveResult moveresult;
201 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
202 // Maximum movement without glitches
203 f32 pos_max_d = BS*0.25;
205 if(m_speed_f.getLength()*dtime > pos_max_d)
206 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
207 v3f pos_f = getBasePosition();
208 v3f pos_f_old = pos_f;
209 v3f accel_f = v3f(0,0,0);
211 IGameDef *gamedef = m_env->getGameDef();
212 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
213 pos_max_d, box, stepheight, dtime,
214 pos_f, m_speed_f, accel_f);
216 if(send_recommended == false)
219 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
221 setBasePosition(pos_f);
222 m_last_sent_position = pos_f;
224 std::ostringstream os(std::ios::binary);
225 // command (0 = update position)
228 writeV3F1000(os, m_base_position);
229 // create message and add to list
230 ActiveObjectMessage aom(getId(), false, os.str());
231 m_messages_out.push_back(aom);
233 if(m_itemstring_changed)
235 m_itemstring_changed = false;
237 std::ostringstream os(std::ios::binary);
238 // command (1 = update itemstring)
241 os<<serializeString(m_itemstring);
242 // create message and add to list
243 ActiveObjectMessage aom(getId(), false, os.str());
244 m_messages_out.push_back(aom);
248 std::string getClientInitializationData()
250 std::ostringstream os(std::ios::binary);
254 writeV3F1000(os, m_base_position);
256 os<<serializeString(m_itemstring);
260 std::string getStaticData()
262 infostream<<__FUNCTION_NAME<<std::endl;
263 std::ostringstream os(std::ios::binary);
267 os<<serializeString(m_itemstring);
271 ItemStack createItemStack()
274 IItemDefManager *idef = m_env->getGameDef()->idef();
276 item.deSerialize(m_itemstring, idef);
277 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
278 <<"\" -> item=\""<<item.getItemString()<<"\""
282 catch(SerializationError &e)
284 infostream<<__FUNCTION_NAME<<": serialization error: "
285 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
291 const ToolCapabilities *toolcap,
292 ServerActiveObject *puncher,
293 float time_from_last_punch)
295 // Take item into inventory
296 ItemStack item = createItemStack();
297 Inventory *inv = puncher->getInventory();
300 std::string wieldlist = puncher->getWieldList();
301 ItemStack leftover = inv->addItem(wieldlist, item);
302 puncher->setInventoryModified();
309 m_itemstring = leftover.getItemString();
310 m_itemstring_changed = true;
319 std::string m_itemstring;
320 bool m_itemstring_changed;
322 v3f m_last_sent_position;
323 IntervalLimiter m_move_interval;
326 // Prototype (registers item for deserialization)
327 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
329 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
330 const std::string itemstring)
332 return new ItemSAO(env, pos, itemstring);
339 // Prototype (registers item for deserialization)
340 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
342 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
343 const std::string &name, const std::string &state):
344 ServerActiveObject(env, pos),
350 m_acceleration(0,0,0),
352 m_properties_sent(true),
354 m_last_sent_position(0,0,0),
355 m_last_sent_velocity(0,0,0),
356 m_last_sent_position_timer(0),
357 m_last_sent_move_precision(0),
358 m_armor_groups_sent(false),
359 m_animations_sent(false),
360 m_animations_bone_sent(false),
361 m_attachment_sent(false)
363 // Only register type if no environment supplied
365 ServerActiveObject::registerType(getType(), create);
369 // Initialize something to armor groups
370 m_armor_groups["fleshy"] = 3;
371 m_armor_groups["snappy"] = 2;
374 LuaEntitySAO::~LuaEntitySAO()
377 lua_State *L = m_env->getLua();
378 scriptapi_luaentity_rm(L, m_id);
382 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
384 ServerActiveObject::addedToEnvironment(dtime_s);
386 // Create entity from name
387 lua_State *L = m_env->getLua();
388 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
392 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
393 // Initialize HP from properties
394 m_hp = m_prop.hp_max;
395 // Activate entity, supplying serialized state
396 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
400 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
401 const std::string &data)
409 std::istringstream is(data, std::ios::binary);
411 u8 version = readU8(is);
412 // check if version is supported
414 name = deSerializeString(is);
415 state = deSerializeLongString(is);
417 else if(version == 1){
418 name = deSerializeString(is);
419 state = deSerializeLongString(is);
421 velocity = readV3F1000(is);
426 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
427 <<state<<"\")"<<std::endl;
428 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
430 sao->m_velocity = velocity;
435 bool LuaEntitySAO::isAttached()
437 if(!m_attachment_parent_id)
439 // Check if the parent still exists
440 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
446 void LuaEntitySAO::step(float dtime, bool send_recommended)
448 if(!m_properties_sent)
450 m_properties_sent = true;
451 std::string str = getPropertyPacket();
452 // create message and add to list
453 ActiveObjectMessage aom(getId(), true, str);
454 m_messages_out.push_back(aom);
457 // If attached, check that our parent is still there. If it isn't, detach.
458 if(m_attachment_parent_id && !isAttached())
460 m_attachment_parent_id = 0;
461 m_attachment_bone = "";
462 m_attachment_position = v3f(0,0,0);
463 m_attachment_rotation = v3f(0,0,0);
464 sendPosition(false, true);
467 m_last_sent_position_timer += dtime;
469 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
470 // If the object gets detached this comes into effect automatically from the last known origin
473 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
474 m_base_position = pos;
475 m_velocity = v3f(0,0,0);
476 m_acceleration = v3f(0,0,0);
481 core::aabbox3d<f32> box = m_prop.collisionbox;
484 collisionMoveResult moveresult;
485 f32 pos_max_d = BS*0.25; // Distance per iteration
486 f32 stepheight = 0; // Maximum climbable step height
487 v3f p_pos = m_base_position;
488 v3f p_velocity = m_velocity;
489 v3f p_acceleration = m_acceleration;
490 IGameDef *gamedef = m_env->getGameDef();
491 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
492 pos_max_d, box, stepheight, dtime,
493 p_pos, p_velocity, p_acceleration);
495 m_base_position = p_pos;
496 m_velocity = p_velocity;
497 m_acceleration = p_acceleration;
499 m_base_position += dtime * m_velocity + 0.5 * dtime
500 * dtime * m_acceleration;
501 m_velocity += dtime * m_acceleration;
506 lua_State *L = m_env->getLua();
507 scriptapi_luaentity_step(L, m_id, dtime);
510 if(send_recommended == false)
515 // TODO: force send when acceleration changes enough?
516 float minchange = 0.2*BS;
517 if(m_last_sent_position_timer > 1.0){
519 } else if(m_last_sent_position_timer > 0.2){
522 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
523 move_d += m_last_sent_move_precision;
524 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
525 if(move_d > minchange || vel_d > minchange ||
526 fabs(m_yaw - m_last_sent_yaw) > 1.0){
527 sendPosition(true, false);
531 if(m_armor_groups_sent == false){
532 m_armor_groups_sent = true;
533 std::string str = gob_cmd_update_armor_groups(
535 // create message and add to list
536 ActiveObjectMessage aom(getId(), true, str);
537 m_messages_out.push_back(aom);
540 if(m_animations_sent == false){
541 m_animations_sent = true;
542 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
543 // create message and add to list
544 ActiveObjectMessage aom(getId(), true, str);
545 m_messages_out.push_back(aom);
548 if(m_animations_bone_sent == false){
549 m_animations_bone_sent = true;
550 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
551 std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
552 // create message and add to list
553 ActiveObjectMessage aom(getId(), true, str);
554 m_messages_out.push_back(aom);
558 if(m_attachment_sent == false){
559 m_attachment_sent = true;
560 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
561 // create message and add to list
562 ActiveObjectMessage aom(getId(), true, str);
563 m_messages_out.push_back(aom);
567 std::string LuaEntitySAO::getClientInitializationData()
569 std::ostringstream os(std::ios::binary);
570 writeU8(os, 0); // version
571 os<<serializeString(""); // name
572 writeS16(os, getId()); //id
573 writeU8(os, 0); // is_player
574 writeV3F1000(os, m_base_position);
575 writeF1000(os, m_yaw);
578 writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
579 os<<serializeLongString(getPropertyPacket()); // message 1
580 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
581 os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
582 if(m_animation_bone.size()){
583 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
584 os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
587 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
593 std::string LuaEntitySAO::getStaticData()
595 verbosestream<<__FUNCTION_NAME<<std::endl;
596 std::ostringstream os(std::ios::binary);
600 os<<serializeString(m_init_name);
603 lua_State *L = m_env->getLua();
604 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
605 os<<serializeLongString(state);
607 os<<serializeLongString(m_init_state);
612 writeV3F1000(os, m_velocity);
614 writeF1000(os, m_yaw);
618 int LuaEntitySAO::punch(v3f dir,
619 const ToolCapabilities *toolcap,
620 ServerActiveObject *puncher,
621 float time_from_last_punch)
624 // Delete unknown LuaEntities when punched
629 // It's best that attachments cannot be punched
633 ItemStack *punchitem = NULL;
634 ItemStack punchitem_static;
636 punchitem_static = puncher->getWieldedItem();
637 punchitem = &punchitem_static;
640 PunchDamageResult result = getPunchDamage(
644 time_from_last_punch);
648 setHP(getHP() - result.damage);
650 actionstream<<getDescription()<<" punched by "
651 <<puncher->getDescription()<<", damage "<<result.damage
652 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
655 std::string str = gob_cmd_punched(result.damage, getHP());
656 // create message and add to list
657 ActiveObjectMessage aom(getId(), true, str);
658 m_messages_out.push_back(aom);
665 lua_State *L = m_env->getLua();
666 scriptapi_luaentity_punch(L, m_id, puncher,
667 time_from_last_punch, toolcap, dir);
672 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
676 lua_State *L = m_env->getLua();
677 scriptapi_luaentity_rightclick(L, m_id, clicker);
680 void LuaEntitySAO::setPos(v3f pos)
684 m_base_position = pos;
685 sendPosition(false, true);
688 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
692 m_base_position = pos;
694 sendPosition(true, true);
697 float LuaEntitySAO::getMinimumSavedMovement()
702 std::string LuaEntitySAO::getDescription()
704 std::ostringstream os(std::ios::binary);
705 os<<"LuaEntitySAO at (";
706 os<<(m_base_position.X/BS)<<",";
707 os<<(m_base_position.Y/BS)<<",";
708 os<<(m_base_position.Z/BS);
713 void LuaEntitySAO::setHP(s16 hp)
719 s16 LuaEntitySAO::getHP() const
724 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
726 m_armor_groups = armor_groups;
727 m_armor_groups_sent = false;
730 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
732 m_animation_frames = frames;
733 m_animation_speed = frame_speed;
734 m_animation_blend = frame_blend;
735 m_animations_sent = false;
738 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
740 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
741 m_animations_bone_sent = false;
744 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
746 // Attachments need to be handled on both the server and client.
747 // If we just attach on the server, we can only copy the position of the parent. Attachments
748 // are still sent to clients at an interval so players might see them lagging, plus we can't
749 // read and attach to skeletal bones.
750 // If we just attach on the client, the server still sees the child at its original location.
751 // This breaks some things so we also give the server the most accurate representation
752 // even if players only see the client changes.
754 m_attachment_parent_id = parent_id;
755 m_attachment_bone = bone;
756 m_attachment_position = position;
757 m_attachment_rotation = rotation;
758 m_attachment_sent = false;
761 ObjectProperties* LuaEntitySAO::accessObjectProperties()
766 void LuaEntitySAO::notifyObjectPropertiesModified()
768 m_properties_sent = false;
771 void LuaEntitySAO::setVelocity(v3f velocity)
773 m_velocity = velocity;
776 v3f LuaEntitySAO::getVelocity()
781 void LuaEntitySAO::setAcceleration(v3f acceleration)
783 m_acceleration = acceleration;
786 v3f LuaEntitySAO::getAcceleration()
788 return m_acceleration;
791 void LuaEntitySAO::setYaw(float yaw)
796 float LuaEntitySAO::getYaw()
801 void LuaEntitySAO::setTextureMod(const std::string &mod)
803 std::string str = gob_cmd_set_texture_mod(mod);
804 // create message and add to list
805 ActiveObjectMessage aom(getId(), true, str);
806 m_messages_out.push_back(aom);
809 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
810 bool select_horiz_by_yawpitch)
812 std::string str = gob_cmd_set_sprite(
816 select_horiz_by_yawpitch
818 // create message and add to list
819 ActiveObjectMessage aom(getId(), true, str);
820 m_messages_out.push_back(aom);
823 std::string LuaEntitySAO::getName()
828 std::string LuaEntitySAO::getPropertyPacket()
830 return gob_cmd_set_properties(m_prop);
833 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
835 // If the object is attached client-side, don't waste bandwidth sending its position to clients
839 m_last_sent_move_precision = m_base_position.getDistanceFrom(
840 m_last_sent_position);
841 m_last_sent_position_timer = 0;
842 m_last_sent_yaw = m_yaw;
843 m_last_sent_position = m_base_position;
844 m_last_sent_velocity = m_velocity;
845 //m_last_sent_acceleration = m_acceleration;
847 float update_interval = m_env->getSendRecommendedInterval();
849 std::string str = gob_cmd_update_position(
858 // create message and add to list
859 ActiveObjectMessage aom(getId(), false, str);
860 m_messages_out.push_back(aom);
867 // No prototype, PlayerSAO does not need to be deserialized
869 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
870 const std::set<std::string> &privs, bool is_singleplayer):
871 ServerActiveObject(env_, v3f(0,0,0)),
875 m_last_good_position(0,0,0),
876 m_last_good_position_age(0),
877 m_time_from_last_punch(0),
878 m_nocheat_dig_pos(32767, 32767, 32767),
879 m_nocheat_dig_time(0),
881 m_position_not_sent(false),
882 m_armor_groups_sent(false),
883 m_properties_sent(true),
885 m_is_singleplayer(is_singleplayer),
886 m_animations_sent(false),
887 m_animations_bone_sent(false),
888 m_attachment_sent(false),
891 m_inventory_not_sent(false),
892 m_hp_not_sent(false),
893 m_wielded_item_not_sent(false)
896 assert(m_peer_id != 0);
897 setBasePosition(m_player->getPosition());
898 m_inventory = &m_player->inventory;
899 m_armor_groups["choppy"] = 2;
900 m_armor_groups["fleshy"] = 3;
902 m_prop.hp_max = PLAYER_MAX_HP;
903 m_prop.physical = false;
905 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
906 // start of default appearance, this should be overwritten by LUA
907 m_prop.visual = "upright_sprite";
908 m_prop.visual_size = v2f(1, 2);
909 m_prop.textures.clear();
910 m_prop.textures.push_back("player.png");
911 m_prop.textures.push_back("player_back.png");
912 m_prop.colors.clear();
913 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
914 m_prop.spritediv = v2s16(1,1);
915 // end of default appearance
916 m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
917 m_prop.makes_footstep_sound = true;
920 PlayerSAO::~PlayerSAO()
922 if(m_inventory != &m_player->inventory)
927 std::string PlayerSAO::getDescription()
929 return std::string("player ") + m_player->getName();
932 // Called after id has been set and has been inserted in environment
933 void PlayerSAO::addedToEnvironment(u32 dtime_s)
935 ServerActiveObject::addedToEnvironment(dtime_s);
936 ServerActiveObject::setBasePosition(m_player->getPosition());
937 m_player->setPlayerSAO(this);
938 m_player->peer_id = m_peer_id;
939 m_last_good_position = m_player->getPosition();
940 m_last_good_position_age = 0.0;
943 // Called before removing from environment
944 void PlayerSAO::removingFromEnvironment()
946 ServerActiveObject::removingFromEnvironment();
947 if(m_player->getPlayerSAO() == this)
949 m_player->setPlayerSAO(NULL);
950 m_player->peer_id = 0;
954 bool PlayerSAO::isStaticAllowed() const
959 bool PlayerSAO::unlimitedTransferDistance() const
961 return g_settings->getBool("unlimited_player_transfer_distance");
964 std::string PlayerSAO::getClientInitializationData()
966 std::ostringstream os(std::ios::binary);
967 writeU8(os, 0); // version
968 os<<serializeString(m_player->getName()); // name
969 writeU8(os, 1); // is_player
970 writeS16(os, getId()); //id
971 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
972 writeF1000(os, m_player->getYaw());
973 writeS16(os, getHP());
975 writeU8(os, 4 + m_animation_bone.size()); // number of messages stuffed in here
976 os<<serializeLongString(getPropertyPacket()); // message 1
977 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
978 os<<serializeLongString(gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend)); // 3
979 if(m_animation_bone.size()){
980 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
981 os<<serializeLongString(gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_animation_bone.size
984 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
990 std::string PlayerSAO::getStaticData()
996 bool PlayerSAO::isAttached()
998 if(!m_attachment_parent_id)
1000 // Check if the parent still exists
1001 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1007 void PlayerSAO::step(float dtime, bool send_recommended)
1009 if(!m_properties_sent)
1011 m_properties_sent = true;
1012 std::string str = getPropertyPacket();
1013 // create message and add to list
1014 ActiveObjectMessage aom(getId(), true, str);
1015 m_messages_out.push_back(aom);
1018 // If attached, check that our parent is still there. If it isn't, detach.
1019 if(m_attachment_parent_id && !isAttached())
1021 m_attachment_parent_id = 0;
1022 m_attachment_bone = "";
1023 m_attachment_position = v3f(0,0,0);
1024 m_attachment_rotation = v3f(0,0,0);
1025 m_player->setPosition(m_last_good_position);
1029 m_time_from_last_punch += dtime;
1030 m_nocheat_dig_time += dtime;
1032 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1033 // If the object gets detached this comes into effect automatically from the last known origin
1036 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1037 m_last_good_position = pos;
1038 m_last_good_position_age = 0;
1039 m_player->setPosition(pos);
1043 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1045 m_last_good_position = m_player->getPosition();
1046 m_last_good_position_age = 0;
1051 Check player movements
1053 NOTE: Actually the server should handle player physics like the
1054 client does and compare player's position to what is calculated
1055 on our side. This is required when eg. players fly due to an
1056 explosion. Altough a node-based alternative might be possible
1057 too, and much more lightweight.
1060 float player_max_speed = 0;
1061 float player_max_speed_up = 0;
1062 if(m_privs.count("fast") != 0){
1064 player_max_speed = BS * 20;
1065 player_max_speed_up = BS * 20;
1068 player_max_speed = BS * 4.0;
1069 player_max_speed_up = BS * 4.0;
1072 player_max_speed *= 2.5;
1073 player_max_speed_up *= 2.5;
1075 m_last_good_position_age += dtime;
1076 if(m_last_good_position_age >= 1.0){
1077 float age = m_last_good_position_age;
1078 v3f diff = (m_player->getPosition() - m_last_good_position);
1079 float d_vert = diff.Y;
1081 float d_horiz = diff.getLength();
1082 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1083 <<(d_horiz/age)<<std::endl;*/
1084 if(d_horiz <= age * player_max_speed &&
1085 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1086 m_last_good_position = m_player->getPosition();
1088 actionstream<<"Player "<<m_player->getName()
1089 <<" moved too fast; resetting position"
1091 m_player->setPosition(m_last_good_position);
1094 m_last_good_position_age = 0;
1099 if(send_recommended == false)
1102 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1103 if(m_position_not_sent && !isAttached())
1105 m_position_not_sent = false;
1106 float update_interval = m_env->getSendRecommendedInterval();
1108 if(isAttached()) // Just in case we ever do send attachment position too
1109 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1111 pos = m_player->getPosition() + v3f(0,BS*1,0);
1112 std::string str = gob_cmd_update_position(
1121 // create message and add to list
1122 ActiveObjectMessage aom(getId(), false, str);
1123 m_messages_out.push_back(aom);
1126 if(m_wielded_item_not_sent)
1128 m_wielded_item_not_sent = false;
1129 // GenericCAO has no special way to show this
1132 if(m_armor_groups_sent == false){
1133 m_armor_groups_sent = true;
1134 std::string str = gob_cmd_update_armor_groups(
1136 // create message and add to list
1137 ActiveObjectMessage aom(getId(), true, str);
1138 m_messages_out.push_back(aom);
1141 if(m_animations_sent == false){
1142 m_animations_sent = true;
1143 std::string str = gob_cmd_update_animations(m_animation_frames, m_animation_speed, m_animation_blend);
1144 // create message and add to list
1145 ActiveObjectMessage aom(getId(), true, str);
1146 m_messages_out.push_back(aom);
1149 if(m_animations_bone_sent == false){
1150 m_animations_bone_sent = true;
1151 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
1152 std::string str = gob_cmd_update_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
1153 // create message and add to list
1154 ActiveObjectMessage aom(getId(), true, str);
1155 m_messages_out.push_back(aom);
1159 if(m_attachment_sent == false){
1160 m_attachment_sent = true;
1161 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1162 // create message and add to list
1163 ActiveObjectMessage aom(getId(), true, str);
1164 m_messages_out.push_back(aom);
1168 void PlayerSAO::setBasePosition(const v3f &position)
1170 // This needs to be ran for attachments too
1171 ServerActiveObject::setBasePosition(position);
1172 m_position_not_sent = true;
1175 void PlayerSAO::setPos(v3f pos)
1179 m_player->setPosition(pos);
1180 // Movement caused by this command is always valid
1181 m_last_good_position = pos;
1182 m_last_good_position_age = 0;
1183 // Force position change on client
1187 void PlayerSAO::moveTo(v3f pos, bool continuous)
1191 m_player->setPosition(pos);
1192 // Movement caused by this command is always valid
1193 m_last_good_position = pos;
1194 m_last_good_position_age = 0;
1195 // Force position change on client
1199 int PlayerSAO::punch(v3f dir,
1200 const ToolCapabilities *toolcap,
1201 ServerActiveObject *puncher,
1202 float time_from_last_punch)
1204 // It's best that attachments cannot be punched
1211 // No effect if PvP disabled
1212 if(g_settings->getBool("enable_pvp") == false){
1213 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1214 std::string str = gob_cmd_punched(0, getHP());
1215 // create message and add to list
1216 ActiveObjectMessage aom(getId(), true, str);
1217 m_messages_out.push_back(aom);
1222 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1223 time_from_last_punch);
1225 actionstream<<"Player "<<m_player->getName()<<" punched by "
1226 <<puncher->getDescription()<<", damage "<<hitparams.hp
1229 setHP(getHP() - hitparams.hp);
1231 if(hitparams.hp != 0)
1233 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1234 // create message and add to list
1235 ActiveObjectMessage aom(getId(), true, str);
1236 m_messages_out.push_back(aom);
1239 return hitparams.wear;
1242 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1246 s16 PlayerSAO::getHP() const
1248 return m_player->hp;
1251 void PlayerSAO::setHP(s16 hp)
1253 s16 oldhp = m_player->hp;
1257 else if(hp > PLAYER_MAX_HP)
1260 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1262 m_hp_not_sent = true; // fix wrong prediction on client
1269 m_hp_not_sent = true;
1271 // On death or reincarnation send an active object message
1272 if((hp == 0) != (oldhp == 0))
1274 // Will send new is_visible value based on (getHP()!=0)
1275 m_properties_sent = false;
1277 std::string str = gob_cmd_punched(0, getHP());
1278 ActiveObjectMessage aom(getId(), true, str);
1279 m_messages_out.push_back(aom);
1283 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1285 m_armor_groups = armor_groups;
1286 m_armor_groups_sent = false;
1289 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1291 // store these so they can be updated to clients
1292 m_animation_frames = frames;
1293 m_animation_speed = frame_speed;
1294 m_animation_blend = frame_blend;
1295 m_animations_sent = false;
1298 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1300 // store these so they can be updated to clients
1301 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
1302 m_animations_bone_sent = false;
1305 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1307 // Attachments need to be handled on both the server and client.
1308 // If we just attach on the server, we can only copy the position of the parent. Attachments
1309 // are still sent to clients at an interval so players might see them lagging, plus we can't
1310 // read and attach to skeletal bones.
1311 // If we just attach on the client, the server still sees the child at its original location.
1312 // This breaks some things so we also give the server the most accurate representation
1313 // even if players only see the client changes.
1315 m_attachment_parent_id = parent_id;
1316 m_attachment_bone = bone;
1317 m_attachment_position = position;
1318 m_attachment_rotation = rotation;
1319 m_attachment_sent = false;
1322 ObjectProperties* PlayerSAO::accessObjectProperties()
1327 void PlayerSAO::notifyObjectPropertiesModified()
1329 m_properties_sent = false;
1332 Inventory* PlayerSAO::getInventory()
1336 const Inventory* PlayerSAO::getInventory() const
1341 InventoryLocation PlayerSAO::getInventoryLocation() const
1343 InventoryLocation loc;
1344 loc.setPlayer(m_player->getName());
1348 void PlayerSAO::setInventoryModified()
1350 m_inventory_not_sent = true;
1353 std::string PlayerSAO::getWieldList() const
1358 int PlayerSAO::getWieldIndex() const
1360 return m_wield_index;
1363 void PlayerSAO::setWieldIndex(int i)
1365 if(i != m_wield_index)
1368 m_wielded_item_not_sent = true;
1372 void PlayerSAO::disconnected()
1376 if(m_player->getPlayerSAO() == this)
1378 m_player->setPlayerSAO(NULL);
1379 m_player->peer_id = 0;
1383 std::string PlayerSAO::getPropertyPacket()
1385 m_prop.is_visible = (getHP() != 0);
1386 return gob_cmd_set_properties(m_prop);