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;
447 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
448 // If the object gets detached this comes into effect automatically from the last known origin
451 v3f pos = m_parent->getBasePosition();
452 m_base_position = pos;
453 m_velocity = v3f(0,0,0);
454 m_acceleration = v3f(0,0,0);
459 core::aabbox3d<f32> box = m_prop.collisionbox;
462 collisionMoveResult moveresult;
463 f32 pos_max_d = BS*0.25; // Distance per iteration
464 f32 stepheight = 0; // Maximum climbable step height
465 v3f p_pos = m_base_position;
466 v3f p_velocity = m_velocity;
467 v3f p_acceleration = m_acceleration;
468 IGameDef *gamedef = m_env->getGameDef();
469 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
470 pos_max_d, box, stepheight, dtime,
471 p_pos, p_velocity, p_acceleration);
473 m_base_position = p_pos;
474 m_velocity = p_velocity;
475 m_acceleration = p_acceleration;
477 m_base_position += dtime * m_velocity + 0.5 * dtime
478 * dtime * m_acceleration;
479 m_velocity += dtime * m_acceleration;
484 lua_State *L = m_env->getLua();
485 scriptapi_luaentity_step(L, m_id, dtime);
488 if(send_recommended == false)
493 // TODO: force send when acceleration changes enough?
494 float minchange = 0.2*BS;
495 if(m_last_sent_position_timer > 1.0){
497 } else if(m_last_sent_position_timer > 0.2){
500 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
501 move_d += m_last_sent_move_precision;
502 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
503 if(move_d > minchange || vel_d > minchange ||
504 fabs(m_yaw - m_last_sent_yaw) > 1.0){
505 sendPosition(true, false);
509 if(m_armor_groups_sent == false){
510 m_armor_groups_sent = true;
511 std::string str = gob_cmd_update_armor_groups(
513 // create message and add to list
514 ActiveObjectMessage aom(getId(), true, str);
515 m_messages_out.push_back(aom);
519 std::string LuaEntitySAO::getClientInitializationData()
521 std::ostringstream os(std::ios::binary);
522 writeU8(os, 0); // version
523 os<<serializeString(""); // name
524 writeS16(os, getId()); //id
525 writeU8(os, 0); // is_player
526 writeV3F1000(os, m_base_position);
527 writeF1000(os, m_yaw);
529 writeU8(os, 2); // number of messages stuffed in here
530 os<<serializeLongString(getPropertyPacket()); // message 1
531 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
536 std::string LuaEntitySAO::getStaticData()
538 verbosestream<<__FUNCTION_NAME<<std::endl;
539 std::ostringstream os(std::ios::binary);
543 os<<serializeString(m_init_name);
546 lua_State *L = m_env->getLua();
547 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
548 os<<serializeLongString(state);
550 os<<serializeLongString(m_init_state);
555 writeV3F1000(os, m_velocity);
557 writeF1000(os, m_yaw);
561 int LuaEntitySAO::punch(v3f dir,
562 const ToolCapabilities *toolcap,
563 ServerActiveObject *puncher,
564 float time_from_last_punch)
567 // Delete unknown LuaEntities when punched
572 // It's best that attachments cannot be punched
576 ItemStack *punchitem = NULL;
577 ItemStack punchitem_static;
579 punchitem_static = puncher->getWieldedItem();
580 punchitem = &punchitem_static;
583 PunchDamageResult result = getPunchDamage(
587 time_from_last_punch);
591 setHP(getHP() - result.damage);
593 actionstream<<getDescription()<<" punched by "
594 <<puncher->getDescription()<<", damage "<<result.damage
595 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
598 std::string str = gob_cmd_punched(result.damage, getHP());
599 // create message and add to list
600 ActiveObjectMessage aom(getId(), true, str);
601 m_messages_out.push_back(aom);
608 lua_State *L = m_env->getLua();
609 scriptapi_luaentity_punch(L, m_id, puncher,
610 time_from_last_punch, toolcap, dir);
615 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
619 lua_State *L = m_env->getLua();
620 scriptapi_luaentity_rightclick(L, m_id, clicker);
623 void LuaEntitySAO::setPos(v3f pos)
627 m_base_position = pos;
628 sendPosition(false, true);
631 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
635 m_base_position = pos;
637 sendPosition(true, true);
640 float LuaEntitySAO::getMinimumSavedMovement()
645 std::string LuaEntitySAO::getDescription()
647 std::ostringstream os(std::ios::binary);
648 os<<"LuaEntitySAO at (";
649 os<<(m_base_position.X/BS)<<",";
650 os<<(m_base_position.Y/BS)<<",";
651 os<<(m_base_position.Z/BS);
656 void LuaEntitySAO::setHP(s16 hp)
662 s16 LuaEntitySAO::getHP() const
667 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
669 m_armor_groups = armor_groups;
670 m_armor_groups_sent = false;
673 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
675 std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
676 // create message and add to list
677 ActiveObjectMessage aom(getId(), true, str);
678 m_messages_out.push_back(aom);
681 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
683 std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
684 // create message and add to list
685 ActiveObjectMessage aom(getId(), true, str);
686 m_messages_out.push_back(aom);
689 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
691 // Attachments need to be handled on both the server and client.
692 // If we just attach on the server, we can only copy the position of the parent. Attachments
693 // are still sent to clients at an interval so players might see them lagging, plus we can't
694 // read and attach to skeletal bones.
695 // If we just attach on the client, the server still sees the child at its original location.
696 // This breaks some things so we also give the server the most accurate representation
697 // even if players only see the client changes.
699 // Server attachment:
702 // Client attachment:
703 std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
704 // create message and add to list
705 ActiveObjectMessage aom(getId(), true, str);
706 m_messages_out.push_back(aom);
709 ObjectProperties* LuaEntitySAO::accessObjectProperties()
714 void LuaEntitySAO::notifyObjectPropertiesModified()
716 m_properties_sent = false;
719 void LuaEntitySAO::setVelocity(v3f velocity)
721 m_velocity = velocity;
724 v3f LuaEntitySAO::getVelocity()
729 void LuaEntitySAO::setAcceleration(v3f acceleration)
731 m_acceleration = acceleration;
734 v3f LuaEntitySAO::getAcceleration()
736 return m_acceleration;
739 void LuaEntitySAO::setYaw(float yaw)
744 float LuaEntitySAO::getYaw()
749 void LuaEntitySAO::setTextureMod(const std::string &mod)
751 std::string str = gob_cmd_set_texture_mod(mod);
752 // create message and add to list
753 ActiveObjectMessage aom(getId(), true, str);
754 m_messages_out.push_back(aom);
757 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
758 bool select_horiz_by_yawpitch)
760 std::string str = gob_cmd_set_sprite(
764 select_horiz_by_yawpitch
766 // create message and add to list
767 ActiveObjectMessage aom(getId(), true, str);
768 m_messages_out.push_back(aom);
771 std::string LuaEntitySAO::getName()
776 std::string LuaEntitySAO::getPropertyPacket()
778 return gob_cmd_set_properties(m_prop);
781 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
783 // If the object is attached client-side, don't waste bandwidth sending its position to clients
787 m_last_sent_move_precision = m_base_position.getDistanceFrom(
788 m_last_sent_position);
789 m_last_sent_position_timer = 0;
790 m_last_sent_yaw = m_yaw;
791 m_last_sent_position = m_base_position;
792 m_last_sent_velocity = m_velocity;
793 //m_last_sent_acceleration = m_acceleration;
795 float update_interval = m_env->getSendRecommendedInterval();
797 std::string str = gob_cmd_update_position(
806 // create message and add to list
807 ActiveObjectMessage aom(getId(), false, str);
808 m_messages_out.push_back(aom);
815 // No prototype, PlayerSAO does not need to be deserialized
817 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
818 const std::set<std::string> &privs, bool is_singleplayer):
819 ServerActiveObject(env_, v3f(0,0,0)),
823 m_last_good_position(0,0,0),
824 m_last_good_position_age(0),
825 m_time_from_last_punch(0),
826 m_nocheat_dig_pos(32767, 32767, 32767),
827 m_nocheat_dig_time(0),
829 m_position_not_sent(false),
830 m_armor_groups_sent(false),
831 m_properties_sent(true),
833 m_is_singleplayer(is_singleplayer),
836 m_inventory_not_sent(false),
837 m_hp_not_sent(false),
838 m_wielded_item_not_sent(false)
841 assert(m_peer_id != 0);
842 setBasePosition(m_player->getPosition());
843 m_inventory = &m_player->inventory;
844 m_armor_groups["choppy"] = 2;
845 m_armor_groups["fleshy"] = 3;
847 m_prop.hp_max = PLAYER_MAX_HP;
848 m_prop.physical = false;
850 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
851 // start of default appearance, this should be overwritten by LUA
852 m_prop.visual = "upright_sprite";
853 m_prop.visual_size = v2f(1, 2);
854 m_prop.textures.clear();
855 m_prop.textures.push_back("player.png");
856 m_prop.textures.push_back("player_back.png");
857 m_prop.colors.clear();
858 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
859 m_prop.spritediv = v2s16(1,1);
860 // end of default appearance
861 m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
862 m_prop.makes_footstep_sound = true;
865 PlayerSAO::~PlayerSAO()
867 if(m_inventory != &m_player->inventory)
872 std::string PlayerSAO::getDescription()
874 return std::string("player ") + m_player->getName();
877 // Called after id has been set and has been inserted in environment
878 void PlayerSAO::addedToEnvironment(u32 dtime_s)
880 ServerActiveObject::addedToEnvironment(dtime_s);
881 ServerActiveObject::setBasePosition(m_player->getPosition());
883 m_player->setPlayerSAO(this);
884 m_player->peer_id = m_peer_id;
885 m_last_good_position = m_player->getPosition();
886 m_last_good_position_age = 0.0;
889 // Called before removing from environment
890 void PlayerSAO::removingFromEnvironment()
892 ServerActiveObject::removingFromEnvironment();
893 if(m_player->getPlayerSAO() == this)
895 m_player->setPlayerSAO(NULL);
896 m_player->peer_id = 0;
900 bool PlayerSAO::isStaticAllowed() const
905 bool PlayerSAO::unlimitedTransferDistance() const
907 return g_settings->getBool("unlimited_player_transfer_distance");
910 std::string PlayerSAO::getClientInitializationData()
912 std::ostringstream os(std::ios::binary);
913 writeU8(os, 0); // version
914 os<<serializeString(m_player->getName()); // name
915 writeU8(os, 1); // is_player
916 writeS16(os, getId()); //id
917 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
918 writeF1000(os, m_player->getYaw());
919 writeS16(os, getHP());
920 writeU8(os, 2); // number of messages stuffed in here
921 os<<serializeLongString(getPropertyPacket()); // message 1
922 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
926 std::string PlayerSAO::getStaticData()
932 void PlayerSAO::step(float dtime, bool send_recommended)
934 if(!m_properties_sent)
936 m_properties_sent = true;
937 std::string str = getPropertyPacket();
938 // create message and add to list
939 ActiveObjectMessage aom(getId(), true, str);
940 m_messages_out.push_back(aom);
943 m_time_from_last_punch += dtime;
944 m_nocheat_dig_time += dtime;
946 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
947 // If the object gets detached this comes into effect automatically from the last known origin
950 v3f pos = m_parent->getBasePosition();
951 m_last_good_position = pos;
952 m_last_good_position_age = 0;
953 m_player->setPosition(pos);
957 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
959 m_last_good_position = m_player->getPosition();
960 m_last_good_position_age = 0;
965 Check player movements
967 NOTE: Actually the server should handle player physics like the
968 client does and compare player's position to what is calculated
969 on our side. This is required when eg. players fly due to an
970 explosion. Altough a node-based alternative might be possible
971 too, and much more lightweight.
974 float player_max_speed = 0;
975 float player_max_speed_up = 0;
976 if(m_privs.count("fast") != 0){
978 player_max_speed = BS * 20;
979 player_max_speed_up = BS * 20;
982 player_max_speed = BS * 4.0;
983 player_max_speed_up = BS * 4.0;
986 player_max_speed *= 2.5;
987 player_max_speed_up *= 2.5;
989 m_last_good_position_age += dtime;
990 if(m_last_good_position_age >= 1.0){
991 float age = m_last_good_position_age;
992 v3f diff = (m_player->getPosition() - m_last_good_position);
993 float d_vert = diff.Y;
995 float d_horiz = diff.getLength();
996 /*infostream<<m_player->getName()<<"'s horizontal speed is "
997 <<(d_horiz/age)<<std::endl;*/
998 if(d_horiz <= age * player_max_speed &&
999 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1000 m_last_good_position = m_player->getPosition();
1002 actionstream<<"Player "<<m_player->getName()
1003 <<" moved too fast; resetting position"
1005 m_player->setPosition(m_last_good_position);
1006 m_teleported = true;
1008 m_last_good_position_age = 0;
1013 if(send_recommended == false)
1016 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1017 if(m_position_not_sent && m_parent == NULL)
1019 m_position_not_sent = false;
1020 float update_interval = m_env->getSendRecommendedInterval();
1022 if(m_parent != NULL) // Just in case we ever do send attachment position too
1023 pos = m_parent->getBasePosition();
1025 pos = m_player->getPosition() + v3f(0,BS*1,0);
1026 std::string str = gob_cmd_update_position(
1035 // create message and add to list
1036 ActiveObjectMessage aom(getId(), false, str);
1037 m_messages_out.push_back(aom);
1040 if(m_wielded_item_not_sent)
1042 m_wielded_item_not_sent = false;
1043 // GenericCAO has no special way to show this
1046 if(m_armor_groups_sent == false){
1047 m_armor_groups_sent = true;
1048 std::string str = gob_cmd_update_armor_groups(
1050 // create message and add to list
1051 ActiveObjectMessage aom(getId(), true, str);
1052 m_messages_out.push_back(aom);
1056 void PlayerSAO::setBasePosition(const v3f &position)
1058 // This needs to be ran for attachments too
1059 ServerActiveObject::setBasePosition(position);
1060 m_position_not_sent = true;
1063 void PlayerSAO::setPos(v3f pos)
1065 if(m_parent != NULL)
1067 m_player->setPosition(pos);
1068 // Movement caused by this command is always valid
1069 m_last_good_position = pos;
1070 m_last_good_position_age = 0;
1071 // Force position change on client
1072 m_teleported = true;
1075 void PlayerSAO::moveTo(v3f pos, bool continuous)
1077 if(m_parent != NULL)
1079 m_player->setPosition(pos);
1080 // Movement caused by this command is always valid
1081 m_last_good_position = pos;
1082 m_last_good_position_age = 0;
1083 // Force position change on client
1084 m_teleported = true;
1087 int PlayerSAO::punch(v3f dir,
1088 const ToolCapabilities *toolcap,
1089 ServerActiveObject *puncher,
1090 float time_from_last_punch)
1092 // It's best that attachments cannot be punched
1093 if(m_parent != NULL)
1099 // No effect if PvP disabled
1100 if(g_settings->getBool("enable_pvp") == false){
1101 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1102 std::string str = gob_cmd_punched(0, getHP());
1103 // create message and add to list
1104 ActiveObjectMessage aom(getId(), true, str);
1105 m_messages_out.push_back(aom);
1110 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1111 time_from_last_punch);
1113 actionstream<<"Player "<<m_player->getName()<<" punched by "
1114 <<puncher->getDescription()<<", damage "<<hitparams.hp
1117 setHP(getHP() - hitparams.hp);
1119 if(hitparams.hp != 0)
1121 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1122 // create message and add to list
1123 ActiveObjectMessage aom(getId(), true, str);
1124 m_messages_out.push_back(aom);
1127 return hitparams.wear;
1130 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1134 s16 PlayerSAO::getHP() const
1136 return m_player->hp;
1139 void PlayerSAO::setHP(s16 hp)
1141 s16 oldhp = m_player->hp;
1145 else if(hp > PLAYER_MAX_HP)
1148 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1150 m_hp_not_sent = true; // fix wrong prediction on client
1157 m_hp_not_sent = true;
1159 // On death or reincarnation send an active object message
1160 if((hp == 0) != (oldhp == 0))
1162 // Will send new is_visible value based on (getHP()!=0)
1163 m_properties_sent = false;
1165 std::string str = gob_cmd_punched(0, getHP());
1166 ActiveObjectMessage aom(getId(), true, str);
1167 m_messages_out.push_back(aom);
1171 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1173 m_armor_groups = armor_groups;
1174 m_armor_groups_sent = false;
1177 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1179 std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
1180 // create message and add to list
1181 ActiveObjectMessage aom(getId(), true, str);
1182 m_messages_out.push_back(aom);
1185 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1187 std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
1188 // create message and add to list
1189 ActiveObjectMessage aom(getId(), true, str);
1190 m_messages_out.push_back(aom);
1193 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1195 // Attachments need to be handled on both the server and client.
1196 // If we just attach on the server, we can only copy the position of the parent. Attachments
1197 // are still sent to clients at an interval so players might see them lagging, plus we can't
1198 // read and attach to skeletal bones.
1199 // If we just attach on the client, the server still sees the child at its original location.
1200 // This breaks some things so we also give the server the most accurate representation
1201 // even if players only see the client changes.
1203 // Server attachment:
1206 // Client attachment:
1207 std::string str = gob_cmd_set_attachment(parent->getId(), bone, position, rotation);
1208 // create message and add to list
1209 ActiveObjectMessage aom(getId(), true, str);
1210 m_messages_out.push_back(aom);
1213 ObjectProperties* PlayerSAO::accessObjectProperties()
1218 void PlayerSAO::notifyObjectPropertiesModified()
1220 m_properties_sent = false;
1223 Inventory* PlayerSAO::getInventory()
1227 const Inventory* PlayerSAO::getInventory() const
1232 InventoryLocation PlayerSAO::getInventoryLocation() const
1234 InventoryLocation loc;
1235 loc.setPlayer(m_player->getName());
1239 void PlayerSAO::setInventoryModified()
1241 m_inventory_not_sent = true;
1244 std::string PlayerSAO::getWieldList() const
1249 int PlayerSAO::getWieldIndex() const
1251 return m_wield_index;
1254 void PlayerSAO::setWieldIndex(int i)
1256 if(i != m_wield_index)
1259 m_wielded_item_not_sent = true;
1263 void PlayerSAO::disconnected()
1267 if(m_player->getPlayerSAO() == this)
1269 m_player->setPlayerSAO(NULL);
1270 m_player->peer_id = 0;
1274 std::string PlayerSAO::getPropertyPacket()
1276 m_prop.is_visible = (getHP() != 0);
1277 return gob_cmd_set_properties(m_prop);