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 // TODO: We shouldn't be sending this when the object is attached, but we can't check m_parent here
222 setBasePosition(pos_f);
223 m_last_sent_position = pos_f;
225 std::ostringstream os(std::ios::binary);
226 // command (0 = update position)
229 writeV3F1000(os, m_base_position);
230 // create message and add to list
231 ActiveObjectMessage aom(getId(), false, os.str());
232 m_messages_out.push_back(aom);
234 if(m_itemstring_changed)
236 m_itemstring_changed = false;
238 std::ostringstream os(std::ios::binary);
239 // command (1 = update itemstring)
242 os<<serializeString(m_itemstring);
243 // create message and add to list
244 ActiveObjectMessage aom(getId(), false, os.str());
245 m_messages_out.push_back(aom);
249 std::string getClientInitializationData()
251 std::ostringstream os(std::ios::binary);
255 writeV3F1000(os, m_base_position);
257 os<<serializeString(m_itemstring);
261 std::string getStaticData()
263 infostream<<__FUNCTION_NAME<<std::endl;
264 std::ostringstream os(std::ios::binary);
268 os<<serializeString(m_itemstring);
272 ItemStack createItemStack()
275 IItemDefManager *idef = m_env->getGameDef()->idef();
277 item.deSerialize(m_itemstring, idef);
278 infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
279 <<"\" -> item=\""<<item.getItemString()<<"\""
283 catch(SerializationError &e)
285 infostream<<__FUNCTION_NAME<<": serialization error: "
286 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
292 const ToolCapabilities *toolcap,
293 ServerActiveObject *puncher,
294 float time_from_last_punch)
296 // Take item into inventory
297 ItemStack item = createItemStack();
298 Inventory *inv = puncher->getInventory();
301 std::string wieldlist = puncher->getWieldList();
302 ItemStack leftover = inv->addItem(wieldlist, item);
303 puncher->setInventoryModified();
310 m_itemstring = leftover.getItemString();
311 m_itemstring_changed = true;
320 std::string m_itemstring;
321 bool m_itemstring_changed;
323 v3f m_last_sent_position;
324 IntervalLimiter m_move_interval;
327 // Prototype (registers item for deserialization)
328 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
330 ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
331 const std::string itemstring)
333 return new ItemSAO(env, pos, itemstring);
340 // Prototype (registers item for deserialization)
341 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
343 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
344 const std::string &name, const std::string &state):
345 ServerActiveObject(env, pos),
351 m_acceleration(0,0,0),
353 m_properties_sent(true),
355 m_last_sent_position(0,0,0),
356 m_last_sent_velocity(0,0,0),
357 m_last_sent_position_timer(0),
358 m_last_sent_move_precision(0),
359 m_armor_groups_sent(false)
361 // Only register type if no environment supplied
363 ServerActiveObject::registerType(getType(), create);
367 // Initialize something to armor groups
368 m_armor_groups["fleshy"] = 3;
369 m_armor_groups["snappy"] = 2;
372 LuaEntitySAO::~LuaEntitySAO()
375 lua_State *L = m_env->getLua();
376 scriptapi_luaentity_rm(L, m_id);
380 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
382 ServerActiveObject::addedToEnvironment(dtime_s);
384 // Create entity from name
385 lua_State *L = m_env->getLua();
386 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
391 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
392 // Initialize HP from properties
393 m_hp = m_prop.hp_max;
394 // Activate entity, supplying serialized state
395 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
399 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
400 const std::string &data)
408 std::istringstream is(data, std::ios::binary);
410 u8 version = readU8(is);
411 // check if version is supported
413 name = deSerializeString(is);
414 state = deSerializeLongString(is);
416 else if(version == 1){
417 name = deSerializeString(is);
418 state = deSerializeLongString(is);
420 velocity = readV3F1000(is);
425 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
426 <<state<<"\")"<<std::endl;
427 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
429 sao->m_velocity = velocity;
434 void LuaEntitySAO::step(float dtime, bool send_recommended)
436 if(!m_properties_sent)
438 m_properties_sent = true;
439 std::string str = getPropertyPacket();
440 // create message and add to list
441 ActiveObjectMessage aom(getId(), true, str);
442 m_messages_out.push_back(aom);
445 m_last_sent_position_timer += dtime;
449 // REMAINING ATTACHMENT ISSUES:
450 // This is causing a segmentation fault, investigate why!
451 //m_base_position = m_parent->getBasePosition();
452 m_velocity = v3f(0,0,0);
453 m_acceleration = v3f(0,0,0);
458 core::aabbox3d<f32> box = m_prop.collisionbox;
461 collisionMoveResult moveresult;
462 f32 pos_max_d = BS*0.25; // Distance per iteration
463 f32 stepheight = 0; // Maximum climbable step height
464 v3f p_pos = m_base_position;
465 v3f p_velocity = m_velocity;
466 v3f p_acceleration = m_acceleration;
467 IGameDef *gamedef = m_env->getGameDef();
468 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
469 pos_max_d, box, stepheight, dtime,
470 p_pos, p_velocity, p_acceleration);
472 m_base_position = p_pos;
473 m_velocity = p_velocity;
474 m_acceleration = p_acceleration;
476 m_base_position += dtime * m_velocity + 0.5 * dtime
477 * dtime * m_acceleration;
478 m_velocity += dtime * m_acceleration;
483 lua_State *L = m_env->getLua();
484 scriptapi_luaentity_step(L, m_id, dtime);
487 if(send_recommended == false)
490 // TODO: force send when acceleration changes enough?
491 float minchange = 0.2*BS;
492 if(m_last_sent_position_timer > 1.0){
494 } else if(m_last_sent_position_timer > 0.2){
497 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
498 move_d += m_last_sent_move_precision;
499 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
500 if(move_d > minchange || vel_d > minchange ||
501 fabs(m_yaw - m_last_sent_yaw) > 1.0){
502 sendPosition(true, false);
505 if(m_armor_groups_sent == false){
506 m_armor_groups_sent = true;
507 std::string str = gob_cmd_update_armor_groups(
509 // create message and add to list
510 ActiveObjectMessage aom(getId(), true, str);
511 m_messages_out.push_back(aom);
515 std::string LuaEntitySAO::getClientInitializationData()
517 std::ostringstream os(std::ios::binary);
518 writeU8(os, 0); // version
519 os<<serializeString(""); // name
520 writeS16(os, getId()); //id
521 writeU8(os, 0); // is_player
522 writeV3F1000(os, m_base_position);
523 writeF1000(os, m_yaw);
525 writeU8(os, 2); // number of messages stuffed in here
526 os<<serializeLongString(getPropertyPacket()); // message 1
527 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
532 std::string LuaEntitySAO::getStaticData()
534 verbosestream<<__FUNCTION_NAME<<std::endl;
535 std::ostringstream os(std::ios::binary);
539 os<<serializeString(m_init_name);
542 lua_State *L = m_env->getLua();
543 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
544 os<<serializeLongString(state);
546 os<<serializeLongString(m_init_state);
551 writeV3F1000(os, m_velocity);
553 writeF1000(os, m_yaw);
557 int LuaEntitySAO::punch(v3f dir,
558 const ToolCapabilities *toolcap,
559 ServerActiveObject *puncher,
560 float time_from_last_punch)
563 // Delete unknown LuaEntities when punched
568 // It's best that attachments cannot be punched
572 ItemStack *punchitem = NULL;
573 ItemStack punchitem_static;
575 punchitem_static = puncher->getWieldedItem();
576 punchitem = &punchitem_static;
579 PunchDamageResult result = getPunchDamage(
583 time_from_last_punch);
587 setHP(getHP() - result.damage);
589 actionstream<<getDescription()<<" punched by "
590 <<puncher->getDescription()<<", damage "<<result.damage
591 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
594 std::string str = gob_cmd_punched(result.damage, getHP());
595 // create message and add to list
596 ActiveObjectMessage aom(getId(), true, str);
597 m_messages_out.push_back(aom);
604 lua_State *L = m_env->getLua();
605 scriptapi_luaentity_punch(L, m_id, puncher,
606 time_from_last_punch, toolcap, dir);
611 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
615 lua_State *L = m_env->getLua();
616 scriptapi_luaentity_rightclick(L, m_id, clicker);
619 void LuaEntitySAO::setPos(v3f pos)
623 m_base_position = pos;
624 sendPosition(false, true);
627 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
631 m_base_position = pos;
633 sendPosition(true, true);
636 float LuaEntitySAO::getMinimumSavedMovement()
641 std::string LuaEntitySAO::getDescription()
643 std::ostringstream os(std::ios::binary);
644 os<<"LuaEntitySAO at (";
645 os<<(m_base_position.X/BS)<<",";
646 os<<(m_base_position.Y/BS)<<",";
647 os<<(m_base_position.Z/BS);
652 void LuaEntitySAO::setHP(s16 hp)
658 s16 LuaEntitySAO::getHP() const
663 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
665 m_armor_groups = armor_groups;
666 m_armor_groups_sent = false;
669 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
671 std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
672 // create message and add to list
673 ActiveObjectMessage aom(getId(), true, str);
674 m_messages_out.push_back(aom);
677 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
679 std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
680 // create message and add to list
681 ActiveObjectMessage aom(getId(), true, str);
682 m_messages_out.push_back(aom);
685 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
687 // Attachments need to be handled on both the server and client.
688 // If we just attach on the server, we can only copy the position of the parent. Attachments
689 // are still sent to clients at an interval so players would see them following the parent
690 // instead of sticking to it, plus we can't read and attach to skeletal bones.
691 // If we just attach on the client, the server still sees the child at its original location.
692 // This can break some things, so we also give the server the most accurate representation
693 // even if players will only see the client changes since they override server-sent position.
695 // Server attachment:
698 // Client attachment:
699 std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
700 // create message and add to list
701 ActiveObjectMessage aom(getId(), true, str);
702 m_messages_out.push_back(aom);
705 ObjectProperties* LuaEntitySAO::accessObjectProperties()
710 void LuaEntitySAO::notifyObjectPropertiesModified()
712 m_properties_sent = false;
715 void LuaEntitySAO::setVelocity(v3f velocity)
717 m_velocity = velocity;
720 v3f LuaEntitySAO::getVelocity()
725 void LuaEntitySAO::setAcceleration(v3f acceleration)
727 m_acceleration = acceleration;
730 v3f LuaEntitySAO::getAcceleration()
732 return m_acceleration;
735 void LuaEntitySAO::setYaw(float yaw)
740 float LuaEntitySAO::getYaw()
745 void LuaEntitySAO::setTextureMod(const std::string &mod)
747 std::string str = gob_cmd_set_texture_mod(mod);
748 // create message and add to list
749 ActiveObjectMessage aom(getId(), true, str);
750 m_messages_out.push_back(aom);
753 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
754 bool select_horiz_by_yawpitch)
756 std::string str = gob_cmd_set_sprite(
760 select_horiz_by_yawpitch
762 // create message and add to list
763 ActiveObjectMessage aom(getId(), true, str);
764 m_messages_out.push_back(aom);
767 std::string LuaEntitySAO::getName()
772 std::string LuaEntitySAO::getPropertyPacket()
774 return gob_cmd_set_properties(m_prop);
777 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
782 m_last_sent_move_precision = m_base_position.getDistanceFrom(
783 m_last_sent_position);
784 m_last_sent_position_timer = 0;
785 m_last_sent_yaw = m_yaw;
786 m_last_sent_position = m_base_position;
787 m_last_sent_velocity = m_velocity;
788 //m_last_sent_acceleration = m_acceleration;
790 float update_interval = m_env->getSendRecommendedInterval();
792 std::string str = gob_cmd_update_position(
801 // create message and add to list
802 ActiveObjectMessage aom(getId(), false, str);
803 m_messages_out.push_back(aom);
810 // No prototype, PlayerSAO does not need to be deserialized
812 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
813 const std::set<std::string> &privs, bool is_singleplayer):
814 ServerActiveObject(env_, v3f(0,0,0)),
818 m_last_good_position(0,0,0),
819 m_last_good_position_age(0),
820 m_time_from_last_punch(0),
821 m_nocheat_dig_pos(32767, 32767, 32767),
822 m_nocheat_dig_time(0),
824 m_position_not_sent(false),
825 m_armor_groups_sent(false),
826 m_properties_sent(true),
828 m_is_singleplayer(is_singleplayer),
831 m_inventory_not_sent(false),
832 m_hp_not_sent(false),
833 m_wielded_item_not_sent(false)
836 assert(m_peer_id != 0);
837 setBasePosition(m_player->getPosition());
838 m_inventory = &m_player->inventory;
839 m_armor_groups["choppy"] = 2;
840 m_armor_groups["fleshy"] = 3;
842 m_prop.hp_max = PLAYER_MAX_HP;
843 m_prop.physical = false;
845 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
846 // start of default appearance, this should be overwritten by LUA
847 m_prop.visual = "upright_sprite";
848 m_prop.visual_size = v2f(1, 2);
849 m_prop.textures.clear();
850 m_prop.textures.push_back("player.png");
851 m_prop.textures.push_back("player_back.png");
852 m_prop.colors.clear();
853 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
854 m_prop.spritediv = v2s16(1,1);
855 // end of default appearance
856 m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
857 m_prop.makes_footstep_sound = true;
860 PlayerSAO::~PlayerSAO()
862 if(m_inventory != &m_player->inventory)
867 std::string PlayerSAO::getDescription()
869 return std::string("player ") + m_player->getName();
872 // Called after id has been set and has been inserted in environment
873 void PlayerSAO::addedToEnvironment(u32 dtime_s)
875 ServerActiveObject::addedToEnvironment(dtime_s);
876 ServerActiveObject::setBasePosition(m_player->getPosition());
878 m_player->setPlayerSAO(this);
879 m_player->peer_id = m_peer_id;
880 m_last_good_position = m_player->getPosition();
881 m_last_good_position_age = 0.0;
884 // Called before removing from environment
885 void PlayerSAO::removingFromEnvironment()
887 ServerActiveObject::removingFromEnvironment();
888 if(m_player->getPlayerSAO() == this)
890 m_player->setPlayerSAO(NULL);
891 m_player->peer_id = 0;
895 bool PlayerSAO::isStaticAllowed() const
900 bool PlayerSAO::unlimitedTransferDistance() const
902 return g_settings->getBool("unlimited_player_transfer_distance");
905 std::string PlayerSAO::getClientInitializationData()
907 std::ostringstream os(std::ios::binary);
908 writeU8(os, 0); // version
909 os<<serializeString(m_player->getName()); // name
910 writeU8(os, 1); // is_player
911 writeS16(os, getId()); //id
912 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
913 writeF1000(os, m_player->getYaw());
914 writeS16(os, getHP());
915 writeU8(os, 2); // number of messages stuffed in here
916 os<<serializeLongString(getPropertyPacket()); // message 1
917 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
921 std::string PlayerSAO::getStaticData()
927 void PlayerSAO::step(float dtime, bool send_recommended)
929 if(!m_properties_sent)
931 m_properties_sent = true;
932 std::string str = getPropertyPacket();
933 // create message and add to list
934 ActiveObjectMessage aom(getId(), true, str);
935 m_messages_out.push_back(aom);
938 m_time_from_last_punch += dtime;
939 m_nocheat_dig_time += dtime;
943 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
945 m_last_good_position = m_player->getPosition();
946 m_last_good_position_age = 0;
951 Check player movements
953 NOTE: Actually the server should handle player physics like the
954 client does and compare player's position to what is calculated
955 on our side. This is required when eg. players fly due to an
956 explosion. Altough a node-based alternative might be possible
957 too, and much more lightweight.
960 float player_max_speed = 0;
961 float player_max_speed_up = 0;
962 if(m_privs.count("fast") != 0){
964 player_max_speed = BS * 20;
965 player_max_speed_up = BS * 20;
968 player_max_speed = BS * 4.0;
969 player_max_speed_up = BS * 4.0;
972 player_max_speed *= 2.5;
973 player_max_speed_up *= 2.5;
975 m_last_good_position_age += dtime;
976 if(m_last_good_position_age >= 1.0){
977 float age = m_last_good_position_age;
978 v3f diff = (m_player->getPosition() - m_last_good_position);
979 float d_vert = diff.Y;
981 float d_horiz = diff.getLength();
982 /*infostream<<m_player->getName()<<"'s horizontal speed is "
983 <<(d_horiz/age)<<std::endl;*/
984 if(d_horiz <= age * player_max_speed &&
985 (d_vert < 0 || d_vert < age * player_max_speed_up)){
986 m_last_good_position = m_player->getPosition();
988 actionstream<<"Player "<<m_player->getName()
989 <<" moved too fast; resetting position"
991 m_player->setPosition(m_last_good_position);
994 m_last_good_position_age = 0;
999 if(send_recommended == false)
1002 // If the object is attached client-side, don't waste bandwidth and send its position to clients
1003 if(m_position_not_sent && m_parent == NULL)
1005 m_position_not_sent = false;
1006 float update_interval = m_env->getSendRecommendedInterval();
1008 // REMAINING ATTACHMENT ISSUES:
1009 // This is causing a segmentation fault, investigate why!
1010 if(m_parent != NULL)
1011 pos = m_parent->getBasePosition();
1013 pos = m_player->getPosition() + v3f(0,BS*1,0);
1014 std::string str = gob_cmd_update_position(
1023 // create message and add to list
1024 ActiveObjectMessage aom(getId(), false, str);
1025 m_messages_out.push_back(aom);
1028 if(m_wielded_item_not_sent)
1030 m_wielded_item_not_sent = false;
1031 // GenericCAO has no special way to show this
1034 if(m_armor_groups_sent == false){
1035 m_armor_groups_sent = true;
1036 std::string str = gob_cmd_update_armor_groups(
1038 // create message and add to list
1039 ActiveObjectMessage aom(getId(), true, str);
1040 m_messages_out.push_back(aom);
1044 void PlayerSAO::setBasePosition(const v3f &position)
1046 if(m_parent != NULL)
1048 ServerActiveObject::setBasePosition(position);
1049 m_position_not_sent = true;
1052 void PlayerSAO::setPos(v3f pos)
1054 if(m_parent != NULL)
1056 m_player->setPosition(pos);
1057 // Movement caused by this command is always valid
1058 m_last_good_position = pos;
1059 m_last_good_position_age = 0;
1060 // Force position change on client
1061 m_teleported = true;
1064 void PlayerSAO::moveTo(v3f pos, bool continuous)
1066 if(m_parent != NULL)
1068 m_player->setPosition(pos);
1069 // Movement caused by this command is always valid
1070 m_last_good_position = pos;
1071 m_last_good_position_age = 0;
1072 // Force position change on client
1073 m_teleported = true;
1076 int PlayerSAO::punch(v3f dir,
1077 const ToolCapabilities *toolcap,
1078 ServerActiveObject *puncher,
1079 float time_from_last_punch)
1081 // It's best that attachments cannot be punched
1082 if(m_parent != NULL)
1088 // No effect if PvP disabled
1089 if(g_settings->getBool("enable_pvp") == false){
1090 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1091 std::string str = gob_cmd_punched(0, getHP());
1092 // create message and add to list
1093 ActiveObjectMessage aom(getId(), true, str);
1094 m_messages_out.push_back(aom);
1099 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1100 time_from_last_punch);
1102 actionstream<<"Player "<<m_player->getName()<<" punched by "
1103 <<puncher->getDescription()<<", damage "<<hitparams.hp
1106 setHP(getHP() - hitparams.hp);
1108 if(hitparams.hp != 0)
1110 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1111 // create message and add to list
1112 ActiveObjectMessage aom(getId(), true, str);
1113 m_messages_out.push_back(aom);
1116 return hitparams.wear;
1119 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1123 s16 PlayerSAO::getHP() const
1125 return m_player->hp;
1128 void PlayerSAO::setHP(s16 hp)
1130 s16 oldhp = m_player->hp;
1134 else if(hp > PLAYER_MAX_HP)
1137 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1139 m_hp_not_sent = true; // fix wrong prediction on client
1146 m_hp_not_sent = true;
1148 // On death or reincarnation send an active object message
1149 if((hp == 0) != (oldhp == 0))
1151 // Will send new is_visible value based on (getHP()!=0)
1152 m_properties_sent = false;
1154 std::string str = gob_cmd_punched(0, getHP());
1155 ActiveObjectMessage aom(getId(), true, str);
1156 m_messages_out.push_back(aom);
1160 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1162 m_armor_groups = armor_groups;
1163 m_armor_groups_sent = false;
1166 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1168 std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
1169 // create message and add to list
1170 ActiveObjectMessage aom(getId(), true, str);
1171 m_messages_out.push_back(aom);
1174 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1176 std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
1177 // create message and add to list
1178 ActiveObjectMessage aom(getId(), true, str);
1179 m_messages_out.push_back(aom);
1182 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1184 // Attachments need to be handled on both the server and client.
1185 // If we just attach on the server, we can only copy the position of the parent. Attachments
1186 // are still sent to clients at an interval so players would see them following the parent
1187 // instead of sticking to it, plus we can't read and attach to skeletal bones.
1188 // If we just attach on the client, the server still sees the child at its original location.
1189 // This can break some things, so we also give the server the most accurate representation
1190 // even if players will only see the client changes since they override server-sent position.
1192 // Server attachment:
1195 // Client attachment:
1196 std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
1197 // create message and add to list
1198 ActiveObjectMessage aom(getId(), true, str);
1199 m_messages_out.push_back(aom);
1202 ObjectProperties* PlayerSAO::accessObjectProperties()
1207 void PlayerSAO::notifyObjectPropertiesModified()
1209 m_properties_sent = false;
1212 Inventory* PlayerSAO::getInventory()
1216 const Inventory* PlayerSAO::getInventory() const
1221 InventoryLocation PlayerSAO::getInventoryLocation() const
1223 InventoryLocation loc;
1224 loc.setPlayer(m_player->getName());
1228 void PlayerSAO::setInventoryModified()
1230 m_inventory_not_sent = true;
1233 std::string PlayerSAO::getWieldList() const
1238 int PlayerSAO::getWieldIndex() const
1240 return m_wield_index;
1243 void PlayerSAO::setWieldIndex(int i)
1245 if(i != m_wield_index)
1248 m_wielded_item_not_sent = true;
1252 void PlayerSAO::disconnected()
1256 if(m_player->getPlayerSAO() == this)
1258 m_player->setPlayerSAO(NULL);
1259 m_player->peer_id = 0;
1263 std::string PlayerSAO::getPropertyPacket()
1265 m_prop.is_visible = (getHP() != 0);
1266 return gob_cmd_set_properties(m_prop);