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),
360 m_animations_sent(false),
361 m_animations_bone_sent(false),
362 m_attachment_sent(false)
364 // Only register type if no environment supplied
366 ServerActiveObject::registerType(getType(), create);
370 // Initialize something to armor groups
371 m_armor_groups["fleshy"] = 3;
372 m_armor_groups["snappy"] = 2;
375 LuaEntitySAO::~LuaEntitySAO()
378 lua_State *L = m_env->getLua();
379 scriptapi_luaentity_rm(L, m_id);
383 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
385 ServerActiveObject::addedToEnvironment(dtime_s);
387 // Create entity from name
388 lua_State *L = m_env->getLua();
389 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
394 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
395 // Initialize HP from properties
396 m_hp = m_prop.hp_max;
397 // Activate entity, supplying serialized state
398 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
402 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
403 const std::string &data)
411 std::istringstream is(data, std::ios::binary);
413 u8 version = readU8(is);
414 // check if version is supported
416 name = deSerializeString(is);
417 state = deSerializeLongString(is);
419 else if(version == 1){
420 name = deSerializeString(is);
421 state = deSerializeLongString(is);
423 velocity = readV3F1000(is);
428 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
429 <<state<<"\")"<<std::endl;
430 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
432 sao->m_velocity = velocity;
437 void LuaEntitySAO::step(float dtime, bool send_recommended)
439 if(!m_properties_sent)
441 m_properties_sent = true;
442 std::string str = getPropertyPacket();
443 // create message and add to list
444 ActiveObjectMessage aom(getId(), true, str);
445 m_messages_out.push_back(aom);
448 m_last_sent_position_timer += dtime;
450 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
451 // If the object gets detached this comes into effect automatically from the last known origin
454 v3f pos = m_parent->getBasePosition();
455 m_base_position = pos;
456 m_velocity = v3f(0,0,0);
457 m_acceleration = v3f(0,0,0);
462 core::aabbox3d<f32> box = m_prop.collisionbox;
465 collisionMoveResult moveresult;
466 f32 pos_max_d = BS*0.25; // Distance per iteration
467 f32 stepheight = 0; // Maximum climbable step height
468 v3f p_pos = m_base_position;
469 v3f p_velocity = m_velocity;
470 v3f p_acceleration = m_acceleration;
471 IGameDef *gamedef = m_env->getGameDef();
472 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
473 pos_max_d, box, stepheight, dtime,
474 p_pos, p_velocity, p_acceleration);
476 m_base_position = p_pos;
477 m_velocity = p_velocity;
478 m_acceleration = p_acceleration;
480 m_base_position += dtime * m_velocity + 0.5 * dtime
481 * dtime * m_acceleration;
482 m_velocity += dtime * m_acceleration;
487 lua_State *L = m_env->getLua();
488 scriptapi_luaentity_step(L, m_id, dtime);
491 if(send_recommended == false)
496 // TODO: force send when acceleration changes enough?
497 float minchange = 0.2*BS;
498 if(m_last_sent_position_timer > 1.0){
500 } else if(m_last_sent_position_timer > 0.2){
503 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
504 move_d += m_last_sent_move_precision;
505 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
506 if(move_d > minchange || vel_d > minchange ||
507 fabs(m_yaw - m_last_sent_yaw) > 1.0){
508 sendPosition(true, false);
512 if(m_armor_groups_sent == false){
513 m_armor_groups_sent = true;
514 std::string str = gob_cmd_update_armor_groups(
516 // create message and add to list
517 ActiveObjectMessage aom(getId(), true, str);
518 m_messages_out.push_back(aom);
521 if(m_animations_sent == false){
522 m_animations_sent = true;
523 std::string str = gob_cmd_set_animations(m_animation_frames, m_animation_speed, m_animation_blend);
524 // create message and add to list
525 ActiveObjectMessage aom(getId(), true, str);
526 m_messages_out.push_back(aom);
529 if(m_animations_bone_sent == false){
530 m_animations_bone_sent = true;
531 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
532 std::string str = gob_cmd_set_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
533 // create message and add to list
534 ActiveObjectMessage aom(getId(), true, str);
535 m_messages_out.push_back(aom);
539 if(m_attachment_sent == false){
540 m_attachment_sent = true;
541 std::string str = gob_cmd_set_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
542 // create message and add to list
543 ActiveObjectMessage aom(getId(), true, str);
544 m_messages_out.push_back(aom);
548 std::string LuaEntitySAO::getClientInitializationData()
550 std::ostringstream os(std::ios::binary);
551 writeU8(os, 0); // version
552 os<<serializeString(""); // name
553 writeS16(os, getId()); //id
554 writeU8(os, 0); // is_player
555 writeV3F1000(os, m_base_position);
556 writeF1000(os, m_yaw);
558 writeU8(os, 2); // number of messages stuffed in here
559 os<<serializeLongString(getPropertyPacket()); // message 1
560 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
565 std::string LuaEntitySAO::getStaticData()
567 verbosestream<<__FUNCTION_NAME<<std::endl;
568 std::ostringstream os(std::ios::binary);
572 os<<serializeString(m_init_name);
575 lua_State *L = m_env->getLua();
576 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
577 os<<serializeLongString(state);
579 os<<serializeLongString(m_init_state);
584 writeV3F1000(os, m_velocity);
586 writeF1000(os, m_yaw);
590 int LuaEntitySAO::punch(v3f dir,
591 const ToolCapabilities *toolcap,
592 ServerActiveObject *puncher,
593 float time_from_last_punch)
596 // Delete unknown LuaEntities when punched
601 // It's best that attachments cannot be punched
605 ItemStack *punchitem = NULL;
606 ItemStack punchitem_static;
608 punchitem_static = puncher->getWieldedItem();
609 punchitem = &punchitem_static;
612 PunchDamageResult result = getPunchDamage(
616 time_from_last_punch);
620 setHP(getHP() - result.damage);
622 actionstream<<getDescription()<<" punched by "
623 <<puncher->getDescription()<<", damage "<<result.damage
624 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
627 std::string str = gob_cmd_punched(result.damage, getHP());
628 // create message and add to list
629 ActiveObjectMessage aom(getId(), true, str);
630 m_messages_out.push_back(aom);
637 lua_State *L = m_env->getLua();
638 scriptapi_luaentity_punch(L, m_id, puncher,
639 time_from_last_punch, toolcap, dir);
644 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
648 lua_State *L = m_env->getLua();
649 scriptapi_luaentity_rightclick(L, m_id, clicker);
652 void LuaEntitySAO::setPos(v3f pos)
656 m_base_position = pos;
657 sendPosition(false, true);
660 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
664 m_base_position = pos;
666 sendPosition(true, true);
669 float LuaEntitySAO::getMinimumSavedMovement()
674 std::string LuaEntitySAO::getDescription()
676 std::ostringstream os(std::ios::binary);
677 os<<"LuaEntitySAO at (";
678 os<<(m_base_position.X/BS)<<",";
679 os<<(m_base_position.Y/BS)<<",";
680 os<<(m_base_position.Z/BS);
685 void LuaEntitySAO::setHP(s16 hp)
691 s16 LuaEntitySAO::getHP() const
696 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
698 m_armor_groups = armor_groups;
699 m_armor_groups_sent = false;
702 void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
704 m_animation_frames = frames;
705 m_animation_speed = frame_speed;
706 m_animation_blend = frame_blend;
707 m_animations_sent = false;
710 void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
712 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
713 m_animations_bone_sent = false;
716 void LuaEntitySAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
718 // Attachments need to be handled on both the server and client.
719 // If we just attach on the server, we can only copy the position of the parent. Attachments
720 // are still sent to clients at an interval so players might see them lagging, plus we can't
721 // read and attach to skeletal bones.
722 // If we just attach on the client, the server still sees the child at its original location.
723 // This breaks some things so we also give the server the most accurate representation
724 // even if players only see the client changes.
726 // Server attachment:
729 // Client attachment:
730 m_attachment_parent_id = parent->getId();
731 m_attachment_bone = bone;
732 m_attachment_position = position;
733 m_attachment_rotation = rotation;
734 m_attachment_sent = false;
737 ObjectProperties* LuaEntitySAO::accessObjectProperties()
742 void LuaEntitySAO::notifyObjectPropertiesModified()
744 m_properties_sent = false;
747 void LuaEntitySAO::setVelocity(v3f velocity)
749 m_velocity = velocity;
752 v3f LuaEntitySAO::getVelocity()
757 void LuaEntitySAO::setAcceleration(v3f acceleration)
759 m_acceleration = acceleration;
762 v3f LuaEntitySAO::getAcceleration()
764 return m_acceleration;
767 void LuaEntitySAO::setYaw(float yaw)
772 float LuaEntitySAO::getYaw()
777 void LuaEntitySAO::setTextureMod(const std::string &mod)
779 std::string str = gob_cmd_set_texture_mod(mod);
780 // create message and add to list
781 ActiveObjectMessage aom(getId(), true, str);
782 m_messages_out.push_back(aom);
785 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
786 bool select_horiz_by_yawpitch)
788 std::string str = gob_cmd_set_sprite(
792 select_horiz_by_yawpitch
794 // create message and add to list
795 ActiveObjectMessage aom(getId(), true, str);
796 m_messages_out.push_back(aom);
799 std::string LuaEntitySAO::getName()
804 std::string LuaEntitySAO::getPropertyPacket()
806 return gob_cmd_set_properties(m_prop);
809 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
811 // If the object is attached client-side, don't waste bandwidth sending its position to clients
815 m_last_sent_move_precision = m_base_position.getDistanceFrom(
816 m_last_sent_position);
817 m_last_sent_position_timer = 0;
818 m_last_sent_yaw = m_yaw;
819 m_last_sent_position = m_base_position;
820 m_last_sent_velocity = m_velocity;
821 //m_last_sent_acceleration = m_acceleration;
823 float update_interval = m_env->getSendRecommendedInterval();
825 std::string str = gob_cmd_update_position(
834 // create message and add to list
835 ActiveObjectMessage aom(getId(), false, str);
836 m_messages_out.push_back(aom);
843 // No prototype, PlayerSAO does not need to be deserialized
845 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
846 const std::set<std::string> &privs, bool is_singleplayer):
847 ServerActiveObject(env_, v3f(0,0,0)),
851 m_last_good_position(0,0,0),
852 m_last_good_position_age(0),
853 m_time_from_last_punch(0),
854 m_nocheat_dig_pos(32767, 32767, 32767),
855 m_nocheat_dig_time(0),
857 m_position_not_sent(false),
858 m_armor_groups_sent(false),
859 m_properties_sent(true),
861 m_is_singleplayer(is_singleplayer),
862 m_animations_sent(false),
863 m_animations_bone_sent(false),
864 m_attachment_sent(false),
867 m_inventory_not_sent(false),
868 m_hp_not_sent(false),
869 m_wielded_item_not_sent(false)
872 assert(m_peer_id != 0);
873 setBasePosition(m_player->getPosition());
874 m_inventory = &m_player->inventory;
875 m_armor_groups["choppy"] = 2;
876 m_armor_groups["fleshy"] = 3;
878 m_prop.hp_max = PLAYER_MAX_HP;
879 m_prop.physical = false;
881 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
882 // start of default appearance, this should be overwritten by LUA
883 m_prop.visual = "upright_sprite";
884 m_prop.visual_size = v2f(1, 2);
885 m_prop.textures.clear();
886 m_prop.textures.push_back("player.png");
887 m_prop.textures.push_back("player_back.png");
888 m_prop.colors.clear();
889 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
890 m_prop.spritediv = v2s16(1,1);
891 // end of default appearance
892 m_prop.is_visible = (getHP() != 0); // TODO: Use a death animation instead for mesh players
893 m_prop.makes_footstep_sound = true;
896 PlayerSAO::~PlayerSAO()
898 if(m_inventory != &m_player->inventory)
903 std::string PlayerSAO::getDescription()
905 return std::string("player ") + m_player->getName();
908 // Called after id has been set and has been inserted in environment
909 void PlayerSAO::addedToEnvironment(u32 dtime_s)
911 ServerActiveObject::addedToEnvironment(dtime_s);
912 ServerActiveObject::setBasePosition(m_player->getPosition());
914 m_player->setPlayerSAO(this);
915 m_player->peer_id = m_peer_id;
916 m_last_good_position = m_player->getPosition();
917 m_last_good_position_age = 0.0;
920 // Called before removing from environment
921 void PlayerSAO::removingFromEnvironment()
923 ServerActiveObject::removingFromEnvironment();
924 if(m_player->getPlayerSAO() == this)
926 m_player->setPlayerSAO(NULL);
927 m_player->peer_id = 0;
931 bool PlayerSAO::isStaticAllowed() const
936 bool PlayerSAO::unlimitedTransferDistance() const
938 return g_settings->getBool("unlimited_player_transfer_distance");
941 std::string PlayerSAO::getClientInitializationData()
943 std::ostringstream os(std::ios::binary);
944 writeU8(os, 0); // version
945 os<<serializeString(m_player->getName()); // name
946 writeU8(os, 1); // is_player
947 writeS16(os, getId()); //id
948 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
949 writeF1000(os, m_player->getYaw());
950 writeS16(os, getHP());
951 writeU8(os, 2); // number of messages stuffed in here
952 os<<serializeLongString(getPropertyPacket()); // message 1
953 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
957 std::string PlayerSAO::getStaticData()
963 void PlayerSAO::step(float dtime, bool send_recommended)
965 if(!m_properties_sent)
967 m_properties_sent = true;
968 std::string str = getPropertyPacket();
969 // create message and add to list
970 ActiveObjectMessage aom(getId(), true, str);
971 m_messages_out.push_back(aom);
974 m_time_from_last_punch += dtime;
975 m_nocheat_dig_time += dtime;
977 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
978 // If the object gets detached this comes into effect automatically from the last known origin
981 v3f pos = m_parent->getBasePosition();
982 m_last_good_position = pos;
983 m_last_good_position_age = 0;
984 m_player->setPosition(pos);
988 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
990 m_last_good_position = m_player->getPosition();
991 m_last_good_position_age = 0;
996 Check player movements
998 NOTE: Actually the server should handle player physics like the
999 client does and compare player's position to what is calculated
1000 on our side. This is required when eg. players fly due to an
1001 explosion. Altough a node-based alternative might be possible
1002 too, and much more lightweight.
1005 float player_max_speed = 0;
1006 float player_max_speed_up = 0;
1007 if(m_privs.count("fast") != 0){
1009 player_max_speed = BS * 20;
1010 player_max_speed_up = BS * 20;
1013 player_max_speed = BS * 4.0;
1014 player_max_speed_up = BS * 4.0;
1017 player_max_speed *= 2.5;
1018 player_max_speed_up *= 2.5;
1020 m_last_good_position_age += dtime;
1021 if(m_last_good_position_age >= 1.0){
1022 float age = m_last_good_position_age;
1023 v3f diff = (m_player->getPosition() - m_last_good_position);
1024 float d_vert = diff.Y;
1026 float d_horiz = diff.getLength();
1027 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1028 <<(d_horiz/age)<<std::endl;*/
1029 if(d_horiz <= age * player_max_speed &&
1030 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1031 m_last_good_position = m_player->getPosition();
1033 actionstream<<"Player "<<m_player->getName()
1034 <<" moved too fast; resetting position"
1036 m_player->setPosition(m_last_good_position);
1037 m_teleported = true;
1039 m_last_good_position_age = 0;
1044 if(send_recommended == false)
1047 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1048 if(m_position_not_sent && m_parent == NULL)
1050 m_position_not_sent = false;
1051 float update_interval = m_env->getSendRecommendedInterval();
1053 if(m_parent != NULL) // Just in case we ever do send attachment position too
1054 pos = m_parent->getBasePosition();
1056 pos = m_player->getPosition() + v3f(0,BS*1,0);
1057 std::string str = gob_cmd_update_position(
1066 // create message and add to list
1067 ActiveObjectMessage aom(getId(), false, str);
1068 m_messages_out.push_back(aom);
1071 if(m_wielded_item_not_sent)
1073 m_wielded_item_not_sent = false;
1074 // GenericCAO has no special way to show this
1077 if(m_armor_groups_sent == false){
1078 m_armor_groups_sent = true;
1079 std::string str = gob_cmd_update_armor_groups(
1081 // create message and add to list
1082 ActiveObjectMessage aom(getId(), true, str);
1083 m_messages_out.push_back(aom);
1086 if(m_animations_sent == false){
1087 m_animations_sent = true;
1088 std::string str = gob_cmd_set_animations(m_animation_frames, m_animation_speed, m_animation_blend);
1089 // create message and add to list
1090 ActiveObjectMessage aom(getId(), true, str);
1091 m_messages_out.push_back(aom);
1094 if(m_animations_bone_sent == false){
1095 m_animations_bone_sent = true;
1096 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_animation_bone.begin(); ii != m_animation_bone.end(); ++ii){
1097 std::string str = gob_cmd_set_bone_posrot((*ii).first, (*ii).second.X, (*ii).second.Y);
1098 // create message and add to list
1099 ActiveObjectMessage aom(getId(), true, str);
1100 m_messages_out.push_back(aom);
1104 if(m_attachment_sent == false){
1105 m_attachment_sent = true;
1106 std::string str = gob_cmd_set_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1107 // create message and add to list
1108 ActiveObjectMessage aom(getId(), true, str);
1109 m_messages_out.push_back(aom);
1113 void PlayerSAO::setBasePosition(const v3f &position)
1115 // This needs to be ran for attachments too
1116 ServerActiveObject::setBasePosition(position);
1117 m_position_not_sent = true;
1120 void PlayerSAO::setPos(v3f pos)
1122 if(m_parent != NULL)
1124 m_player->setPosition(pos);
1125 // Movement caused by this command is always valid
1126 m_last_good_position = pos;
1127 m_last_good_position_age = 0;
1128 // Force position change on client
1129 m_teleported = true;
1132 void PlayerSAO::moveTo(v3f pos, bool continuous)
1134 if(m_parent != NULL)
1136 m_player->setPosition(pos);
1137 // Movement caused by this command is always valid
1138 m_last_good_position = pos;
1139 m_last_good_position_age = 0;
1140 // Force position change on client
1141 m_teleported = true;
1144 int PlayerSAO::punch(v3f dir,
1145 const ToolCapabilities *toolcap,
1146 ServerActiveObject *puncher,
1147 float time_from_last_punch)
1149 // It's best that attachments cannot be punched
1150 if(m_parent != NULL)
1156 // No effect if PvP disabled
1157 if(g_settings->getBool("enable_pvp") == false){
1158 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1159 std::string str = gob_cmd_punched(0, getHP());
1160 // create message and add to list
1161 ActiveObjectMessage aom(getId(), true, str);
1162 m_messages_out.push_back(aom);
1167 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1168 time_from_last_punch);
1170 actionstream<<"Player "<<m_player->getName()<<" punched by "
1171 <<puncher->getDescription()<<", damage "<<hitparams.hp
1174 setHP(getHP() - hitparams.hp);
1176 if(hitparams.hp != 0)
1178 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1179 // create message and add to list
1180 ActiveObjectMessage aom(getId(), true, str);
1181 m_messages_out.push_back(aom);
1184 return hitparams.wear;
1187 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1191 s16 PlayerSAO::getHP() const
1193 return m_player->hp;
1196 void PlayerSAO::setHP(s16 hp)
1198 s16 oldhp = m_player->hp;
1202 else if(hp > PLAYER_MAX_HP)
1205 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1207 m_hp_not_sent = true; // fix wrong prediction on client
1214 m_hp_not_sent = true;
1216 // On death or reincarnation send an active object message
1217 if((hp == 0) != (oldhp == 0))
1219 // Will send new is_visible value based on (getHP()!=0)
1220 m_properties_sent = false;
1222 std::string str = gob_cmd_punched(0, getHP());
1223 ActiveObjectMessage aom(getId(), true, str);
1224 m_messages_out.push_back(aom);
1228 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1230 m_armor_groups = armor_groups;
1231 m_armor_groups_sent = false;
1234 void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
1236 // store these so they can be updated to clients
1237 m_animation_frames = frames;
1238 m_animation_speed = frame_speed;
1239 m_animation_blend = frame_blend;
1240 m_animations_sent = false;
1243 void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
1245 // store these so they can be updated to clients
1246 m_animation_bone[bone] = core::vector2d<v3f>(position, rotation);
1247 m_animations_bone_sent = false;
1250 void PlayerSAO::setAttachment(ServerActiveObject *parent, std::string bone, v3f position, v3f rotation)
1252 // Attachments need to be handled on both the server and client.
1253 // If we just attach on the server, we can only copy the position of the parent. Attachments
1254 // are still sent to clients at an interval so players might see them lagging, plus we can't
1255 // read and attach to skeletal bones.
1256 // If we just attach on the client, the server still sees the child at its original location.
1257 // This breaks some things so we also give the server the most accurate representation
1258 // even if players only see the client changes.
1260 // Server attachment:
1263 // Client attachment:
1264 m_attachment_parent_id = parent->getId();
1265 m_attachment_bone = bone;
1266 m_attachment_position = position;
1267 m_attachment_rotation = rotation;
1268 m_attachment_sent = false;
1271 ObjectProperties* PlayerSAO::accessObjectProperties()
1276 void PlayerSAO::notifyObjectPropertiesModified()
1278 m_properties_sent = false;
1281 Inventory* PlayerSAO::getInventory()
1285 const Inventory* PlayerSAO::getInventory() const
1290 InventoryLocation PlayerSAO::getInventoryLocation() const
1292 InventoryLocation loc;
1293 loc.setPlayer(m_player->getName());
1297 void PlayerSAO::setInventoryModified()
1299 m_inventory_not_sent = true;
1302 std::string PlayerSAO::getWieldList() const
1307 int PlayerSAO::getWieldIndex() const
1309 return m_wield_index;
1312 void PlayerSAO::setWieldIndex(int i)
1314 if(i != m_wield_index)
1317 m_wielded_item_not_sent = true;
1321 void PlayerSAO::disconnected()
1325 if(m_player->getPlayerSAO() == this)
1327 m_player->setPlayerSAO(NULL);
1328 m_player->peer_id = 0;
1332 std::string PlayerSAO::getPropertyPacket()
1334 m_prop.is_visible = (getHP() != 0);
1335 return gob_cmd_set_properties(m_prop);