3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "collision.h"
22 #include "environment.h"
24 #include "main.h" // For g_profiler
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
34 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class DummyLoadSAO : public ServerActiveObject
43 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44 ServerActiveObject(env, pos)
46 ServerActiveObject::registerType(type, create);
48 // Pretend to be the test object (to fool the client)
50 { return ACTIVEOBJECT_TYPE_TEST; }
51 // And never save to disk
52 bool isStaticAllowed() const
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new DummyLoadSAO(env, pos, 0);
61 void step(float dtime, bool send_recommended)
64 infostream<<"DummyLoadSAO step"<<std::endl;
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(u16 protocol_version)
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_animation_speed(0),
360 m_animation_blend(0),
361 m_animation_sent(false),
362 m_bone_position_sent(false),
363 m_attachment_parent_id(0),
364 m_attachment_sent(false)
366 // Only register type if no environment supplied
368 ServerActiveObject::registerType(getType(), create);
372 // Initialize something to armor groups
373 m_armor_groups["fleshy"] = 3;
374 m_armor_groups["snappy"] = 2;
377 LuaEntitySAO::~LuaEntitySAO()
380 lua_State *L = m_env->getLua();
381 scriptapi_luaentity_rm(L, m_id);
385 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
387 ServerActiveObject::addedToEnvironment(dtime_s);
389 // Create entity from name
390 lua_State *L = m_env->getLua();
391 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
395 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
396 // Initialize HP from properties
397 m_hp = m_prop.hp_max;
398 // Activate entity, supplying serialized state
399 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
403 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
404 const std::string &data)
412 std::istringstream is(data, std::ios::binary);
414 u8 version = readU8(is);
415 // check if version is supported
417 name = deSerializeString(is);
418 state = deSerializeLongString(is);
420 else if(version == 1){
421 name = deSerializeString(is);
422 state = deSerializeLongString(is);
424 velocity = readV3F1000(is);
429 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
430 <<state<<"\")"<<std::endl;
431 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
433 sao->m_velocity = velocity;
438 bool LuaEntitySAO::isAttached()
440 if(!m_attachment_parent_id)
442 // Check if the parent still exists
443 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
449 void LuaEntitySAO::step(float dtime, bool send_recommended)
451 if(!m_properties_sent)
453 m_properties_sent = true;
454 std::string str = getPropertyPacket();
455 // create message and add to list
456 ActiveObjectMessage aom(getId(), true, str);
457 m_messages_out.push_back(aom);
460 // If attached, check that our parent is still there. If it isn't, detach.
461 if(m_attachment_parent_id && !isAttached())
463 m_attachment_parent_id = 0;
464 m_attachment_bone = "";
465 m_attachment_position = v3f(0,0,0);
466 m_attachment_rotation = v3f(0,0,0);
467 sendPosition(false, true);
470 m_last_sent_position_timer += dtime;
472 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
473 // If the object gets detached this comes into effect automatically from the last known origin
476 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
477 m_base_position = pos;
478 m_velocity = v3f(0,0,0);
479 m_acceleration = v3f(0,0,0);
484 core::aabbox3d<f32> box = m_prop.collisionbox;
487 collisionMoveResult moveresult;
488 f32 pos_max_d = BS*0.25; // Distance per iteration
489 f32 stepheight = 0; // Maximum climbable step height
490 v3f p_pos = m_base_position;
491 v3f p_velocity = m_velocity;
492 v3f p_acceleration = m_acceleration;
493 IGameDef *gamedef = m_env->getGameDef();
494 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
495 pos_max_d, box, stepheight, dtime,
496 p_pos, p_velocity, p_acceleration);
498 m_base_position = p_pos;
499 m_velocity = p_velocity;
500 m_acceleration = p_acceleration;
502 m_base_position += dtime * m_velocity + 0.5 * dtime
503 * dtime * m_acceleration;
504 m_velocity += dtime * m_acceleration;
509 lua_State *L = m_env->getLua();
510 scriptapi_luaentity_step(L, m_id, dtime);
513 if(send_recommended == false)
518 // TODO: force send when acceleration changes enough?
519 float minchange = 0.2*BS;
520 if(m_last_sent_position_timer > 1.0){
522 } else if(m_last_sent_position_timer > 0.2){
525 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
526 move_d += m_last_sent_move_precision;
527 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
528 if(move_d > minchange || vel_d > minchange ||
529 fabs(m_yaw - m_last_sent_yaw) > 1.0){
530 sendPosition(true, false);
534 if(m_armor_groups_sent == false){
535 m_armor_groups_sent = true;
536 std::string str = gob_cmd_update_armor_groups(
538 // create message and add to list
539 ActiveObjectMessage aom(getId(), true, str);
540 m_messages_out.push_back(aom);
543 if(m_animation_sent == false){
544 m_animation_sent = true;
545 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
546 // create message and add to list
547 ActiveObjectMessage aom(getId(), true, str);
548 m_messages_out.push_back(aom);
551 if(m_bone_position_sent == false){
552 m_bone_position_sent = true;
553 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
554 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
555 // create message and add to list
556 ActiveObjectMessage aom(getId(), true, str);
557 m_messages_out.push_back(aom);
561 if(m_attachment_sent == false){
562 m_attachment_sent = true;
563 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
564 // create message and add to list
565 ActiveObjectMessage aom(getId(), true, str);
566 m_messages_out.push_back(aom);
570 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
572 std::ostringstream os(std::ios::binary);
574 if(protocol_version >= 14)
576 writeU8(os, 1); // version
577 os<<serializeString(""); // name
578 writeU8(os, 0); // is_player
579 writeS16(os, getId()); //id
580 writeV3F1000(os, m_base_position);
581 writeF1000(os, m_yaw);
584 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
585 os<<serializeLongString(getPropertyPacket()); // message 1
586 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
587 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
588 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
589 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
591 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
595 writeU8(os, 0); // version
596 os<<serializeString(""); // name
597 writeU8(os, 0); // is_player
598 writeV3F1000(os, m_base_position);
599 writeF1000(os, m_yaw);
601 writeU8(os, 2); // number of messages stuffed in here
602 os<<serializeLongString(getPropertyPacket()); // message 1
603 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
610 std::string LuaEntitySAO::getStaticData()
612 verbosestream<<__FUNCTION_NAME<<std::endl;
613 std::ostringstream os(std::ios::binary);
617 os<<serializeString(m_init_name);
620 lua_State *L = m_env->getLua();
621 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
622 os<<serializeLongString(state);
624 os<<serializeLongString(m_init_state);
629 writeV3F1000(os, m_velocity);
631 writeF1000(os, m_yaw);
635 int LuaEntitySAO::punch(v3f dir,
636 const ToolCapabilities *toolcap,
637 ServerActiveObject *puncher,
638 float time_from_last_punch)
641 // Delete unknown LuaEntities when punched
646 // It's best that attachments cannot be punched
650 ItemStack *punchitem = NULL;
651 ItemStack punchitem_static;
653 punchitem_static = puncher->getWieldedItem();
654 punchitem = &punchitem_static;
657 PunchDamageResult result = getPunchDamage(
661 time_from_last_punch);
665 setHP(getHP() - result.damage);
667 actionstream<<getDescription()<<" punched by "
668 <<puncher->getDescription()<<", damage "<<result.damage
669 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
672 std::string str = gob_cmd_punched(result.damage, getHP());
673 // create message and add to list
674 ActiveObjectMessage aom(getId(), true, str);
675 m_messages_out.push_back(aom);
682 lua_State *L = m_env->getLua();
683 scriptapi_luaentity_punch(L, m_id, puncher,
684 time_from_last_punch, toolcap, dir);
689 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
693 // It's best that attachments cannot be clicked
696 lua_State *L = m_env->getLua();
697 scriptapi_luaentity_rightclick(L, m_id, clicker);
700 void LuaEntitySAO::setPos(v3f pos)
704 m_base_position = pos;
705 sendPosition(false, true);
708 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
712 m_base_position = pos;
714 sendPosition(true, true);
717 float LuaEntitySAO::getMinimumSavedMovement()
722 std::string LuaEntitySAO::getDescription()
724 std::ostringstream os(std::ios::binary);
725 os<<"LuaEntitySAO at (";
726 os<<(m_base_position.X/BS)<<",";
727 os<<(m_base_position.Y/BS)<<",";
728 os<<(m_base_position.Z/BS);
733 void LuaEntitySAO::setHP(s16 hp)
739 s16 LuaEntitySAO::getHP() const
744 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
746 m_armor_groups = armor_groups;
747 m_armor_groups_sent = false;
750 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
752 m_animation_range = frame_range;
753 m_animation_speed = frame_speed;
754 m_animation_blend = frame_blend;
755 m_animation_sent = false;
758 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
760 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
761 m_bone_position_sent = false;
764 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
766 // Attachments need to be handled on both the server and client.
767 // If we just attach on the server, we can only copy the position of the parent. Attachments
768 // are still sent to clients at an interval so players might see them lagging, plus we can't
769 // read and attach to skeletal bones.
770 // If we just attach on the client, the server still sees the child at its original location.
771 // This breaks some things so we also give the server the most accurate representation
772 // even if players only see the client changes.
774 m_attachment_parent_id = parent_id;
775 m_attachment_bone = bone;
776 m_attachment_position = position;
777 m_attachment_rotation = rotation;
778 m_attachment_sent = false;
781 ObjectProperties* LuaEntitySAO::accessObjectProperties()
786 void LuaEntitySAO::notifyObjectPropertiesModified()
788 m_properties_sent = false;
791 void LuaEntitySAO::setVelocity(v3f velocity)
793 m_velocity = velocity;
796 v3f LuaEntitySAO::getVelocity()
801 void LuaEntitySAO::setAcceleration(v3f acceleration)
803 m_acceleration = acceleration;
806 v3f LuaEntitySAO::getAcceleration()
808 return m_acceleration;
811 void LuaEntitySAO::setYaw(float yaw)
816 float LuaEntitySAO::getYaw()
821 void LuaEntitySAO::setTextureMod(const std::string &mod)
823 std::string str = gob_cmd_set_texture_mod(mod);
824 // create message and add to list
825 ActiveObjectMessage aom(getId(), true, str);
826 m_messages_out.push_back(aom);
829 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
830 bool select_horiz_by_yawpitch)
832 std::string str = gob_cmd_set_sprite(
836 select_horiz_by_yawpitch
838 // create message and add to list
839 ActiveObjectMessage aom(getId(), true, str);
840 m_messages_out.push_back(aom);
843 std::string LuaEntitySAO::getName()
848 std::string LuaEntitySAO::getPropertyPacket()
850 return gob_cmd_set_properties(m_prop);
853 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
855 // If the object is attached client-side, don't waste bandwidth sending its position to clients
859 m_last_sent_move_precision = m_base_position.getDistanceFrom(
860 m_last_sent_position);
861 m_last_sent_position_timer = 0;
862 m_last_sent_yaw = m_yaw;
863 m_last_sent_position = m_base_position;
864 m_last_sent_velocity = m_velocity;
865 //m_last_sent_acceleration = m_acceleration;
867 float update_interval = m_env->getSendRecommendedInterval();
869 std::string str = gob_cmd_update_position(
878 // create message and add to list
879 ActiveObjectMessage aom(getId(), false, str);
880 m_messages_out.push_back(aom);
887 // No prototype, PlayerSAO does not need to be deserialized
889 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
890 const std::set<std::string> &privs, bool is_singleplayer):
891 ServerActiveObject(env_, v3f(0,0,0)),
895 m_last_good_position(0,0,0),
896 m_last_good_position_age(0),
897 m_time_from_last_punch(0),
898 m_nocheat_dig_pos(32767, 32767, 32767),
899 m_nocheat_dig_time(0),
901 m_position_not_sent(false),
902 m_armor_groups_sent(false),
903 m_properties_sent(true),
905 m_is_singleplayer(is_singleplayer),
906 m_animation_sent(false),
907 m_bone_position_sent(false),
908 m_attachment_sent(false),
911 m_inventory_not_sent(false),
912 m_hp_not_sent(false),
913 m_wielded_item_not_sent(false)
916 assert(m_peer_id != 0);
917 setBasePosition(m_player->getPosition());
918 m_inventory = &m_player->inventory;
919 m_armor_groups["choppy"] = 2;
920 m_armor_groups["fleshy"] = 3;
922 m_prop.hp_max = PLAYER_MAX_HP;
923 m_prop.physical = false;
925 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
926 // start of default appearance, this should be overwritten by LUA
927 m_prop.visual = "upright_sprite";
928 m_prop.visual_size = v2f(1, 2);
929 m_prop.textures.clear();
930 m_prop.textures.push_back("player.png");
931 m_prop.textures.push_back("player_back.png");
932 m_prop.colors.clear();
933 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
934 m_prop.spritediv = v2s16(1,1);
935 // end of default appearance
936 m_prop.is_visible = true;
937 m_prop.makes_footstep_sound = true;
940 PlayerSAO::~PlayerSAO()
942 if(m_inventory != &m_player->inventory)
947 std::string PlayerSAO::getDescription()
949 return std::string("player ") + m_player->getName();
952 // Called after id has been set and has been inserted in environment
953 void PlayerSAO::addedToEnvironment(u32 dtime_s)
955 ServerActiveObject::addedToEnvironment(dtime_s);
956 ServerActiveObject::setBasePosition(m_player->getPosition());
957 m_player->setPlayerSAO(this);
958 m_player->peer_id = m_peer_id;
959 m_last_good_position = m_player->getPosition();
960 m_last_good_position_age = 0.0;
963 // Called before removing from environment
964 void PlayerSAO::removingFromEnvironment()
966 ServerActiveObject::removingFromEnvironment();
967 if(m_player->getPlayerSAO() == this)
969 m_player->setPlayerSAO(NULL);
970 m_player->peer_id = 0;
974 bool PlayerSAO::isStaticAllowed() const
979 bool PlayerSAO::unlimitedTransferDistance() const
981 return g_settings->getBool("unlimited_player_transfer_distance");
984 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
986 std::ostringstream os(std::ios::binary);
988 if(protocol_version >= 15)
990 writeU8(os, 1); // version
991 os<<serializeString(m_player->getName()); // name
992 writeU8(os, 1); // is_player
993 writeS16(os, getId()); //id
994 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
995 writeF1000(os, m_player->getYaw());
996 writeS16(os, getHP());
998 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
999 os<<serializeLongString(getPropertyPacket()); // message 1
1000 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1001 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
1002 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1003 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1005 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1009 writeU8(os, 0); // version
1010 os<<serializeString(m_player->getName()); // name
1011 writeU8(os, 1); // is_player
1012 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1013 writeF1000(os, m_player->getYaw());
1014 writeS16(os, getHP());
1015 writeU8(os, 2); // number of messages stuffed in here
1016 os<<serializeLongString(getPropertyPacket()); // message 1
1017 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1024 std::string PlayerSAO::getStaticData()
1030 bool PlayerSAO::isAttached()
1032 if(!m_attachment_parent_id)
1034 // Check if the parent still exists
1035 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1041 void PlayerSAO::step(float dtime, bool send_recommended)
1043 if(!m_properties_sent)
1045 m_properties_sent = true;
1046 std::string str = getPropertyPacket();
1047 // create message and add to list
1048 ActiveObjectMessage aom(getId(), true, str);
1049 m_messages_out.push_back(aom);
1052 // If attached, check that our parent is still there. If it isn't, detach.
1053 if(m_attachment_parent_id && !isAttached())
1055 m_attachment_parent_id = 0;
1056 m_attachment_bone = "";
1057 m_attachment_position = v3f(0,0,0);
1058 m_attachment_rotation = v3f(0,0,0);
1059 m_player->setPosition(m_last_good_position);
1063 m_time_from_last_punch += dtime;
1064 m_nocheat_dig_time += dtime;
1066 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1067 // If the object gets detached this comes into effect automatically from the last known origin
1070 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1071 m_last_good_position = pos;
1072 m_last_good_position_age = 0;
1073 m_player->setPosition(pos);
1077 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1079 m_last_good_position = m_player->getPosition();
1080 m_last_good_position_age = 0;
1085 Check player movements
1087 NOTE: Actually the server should handle player physics like the
1088 client does and compare player's position to what is calculated
1089 on our side. This is required when eg. players fly due to an
1090 explosion. Altough a node-based alternative might be possible
1091 too, and much more lightweight.
1094 float player_max_speed = 0;
1095 float player_max_speed_up = 0;
1096 if(m_privs.count("fast") != 0){
1098 player_max_speed = BS * 20;
1099 player_max_speed_up = BS * 20;
1102 player_max_speed = BS * 4.0;
1103 player_max_speed_up = BS * 4.0;
1106 player_max_speed *= 2.5;
1107 player_max_speed_up *= 2.5;
1109 m_last_good_position_age += dtime;
1110 if(m_last_good_position_age >= 1.0){
1111 float age = m_last_good_position_age;
1112 v3f diff = (m_player->getPosition() - m_last_good_position);
1113 float d_vert = diff.Y;
1115 float d_horiz = diff.getLength();
1116 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1117 <<(d_horiz/age)<<std::endl;*/
1118 if(d_horiz <= age * player_max_speed &&
1119 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1120 m_last_good_position = m_player->getPosition();
1122 actionstream<<"Player "<<m_player->getName()
1123 <<" moved too fast; resetting position"
1125 m_player->setPosition(m_last_good_position);
1128 m_last_good_position_age = 0;
1133 if(send_recommended == false)
1136 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1137 if(m_position_not_sent && !isAttached())
1139 m_position_not_sent = false;
1140 float update_interval = m_env->getSendRecommendedInterval();
1142 if(isAttached()) // Just in case we ever do send attachment position too
1143 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1145 pos = m_player->getPosition() + v3f(0,BS*1,0);
1146 std::string str = gob_cmd_update_position(
1155 // create message and add to list
1156 ActiveObjectMessage aom(getId(), false, str);
1157 m_messages_out.push_back(aom);
1160 if(m_wielded_item_not_sent)
1162 m_wielded_item_not_sent = false;
1163 // GenericCAO has no special way to show this
1166 if(m_armor_groups_sent == false){
1167 m_armor_groups_sent = true;
1168 std::string str = gob_cmd_update_armor_groups(
1170 // create message and add to list
1171 ActiveObjectMessage aom(getId(), true, str);
1172 m_messages_out.push_back(aom);
1175 if(m_animation_sent == false){
1176 m_animation_sent = true;
1177 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1178 // create message and add to list
1179 ActiveObjectMessage aom(getId(), true, str);
1180 m_messages_out.push_back(aom);
1183 if(m_bone_position_sent == false){
1184 m_bone_position_sent = true;
1185 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1186 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1187 // create message and add to list
1188 ActiveObjectMessage aom(getId(), true, str);
1189 m_messages_out.push_back(aom);
1193 if(m_attachment_sent == false){
1194 m_attachment_sent = true;
1195 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1196 // create message and add to list
1197 ActiveObjectMessage aom(getId(), true, str);
1198 m_messages_out.push_back(aom);
1202 void PlayerSAO::setBasePosition(const v3f &position)
1204 // This needs to be ran for attachments too
1205 ServerActiveObject::setBasePosition(position);
1206 m_position_not_sent = true;
1209 void PlayerSAO::setPos(v3f pos)
1213 m_player->setPosition(pos);
1214 // Movement caused by this command is always valid
1215 m_last_good_position = pos;
1216 m_last_good_position_age = 0;
1217 // Force position change on client
1221 void PlayerSAO::moveTo(v3f pos, bool continuous)
1225 m_player->setPosition(pos);
1226 // Movement caused by this command is always valid
1227 m_last_good_position = pos;
1228 m_last_good_position_age = 0;
1229 // Force position change on client
1233 int PlayerSAO::punch(v3f dir,
1234 const ToolCapabilities *toolcap,
1235 ServerActiveObject *puncher,
1236 float time_from_last_punch)
1238 // It's best that attachments cannot be punched
1245 // No effect if PvP disabled
1246 if(g_settings->getBool("enable_pvp") == false){
1247 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1248 std::string str = gob_cmd_punched(0, getHP());
1249 // create message and add to list
1250 ActiveObjectMessage aom(getId(), true, str);
1251 m_messages_out.push_back(aom);
1256 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1257 time_from_last_punch);
1259 actionstream<<"Player "<<m_player->getName()<<" punched by "
1260 <<puncher->getDescription()<<", damage "<<hitparams.hp
1263 setHP(getHP() - hitparams.hp);
1265 if(hitparams.hp != 0)
1267 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1268 // create message and add to list
1269 ActiveObjectMessage aom(getId(), true, str);
1270 m_messages_out.push_back(aom);
1273 return hitparams.wear;
1276 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1280 s16 PlayerSAO::getHP() const
1282 return m_player->hp;
1285 void PlayerSAO::setHP(s16 hp)
1287 s16 oldhp = m_player->hp;
1291 else if(hp > PLAYER_MAX_HP)
1294 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1296 m_hp_not_sent = true; // fix wrong prediction on client
1303 m_hp_not_sent = true;
1305 // On death or reincarnation send an active object message
1306 if((hp == 0) != (oldhp == 0))
1308 // Will send new is_visible value based on (getHP()!=0)
1309 m_properties_sent = false;
1311 std::string str = gob_cmd_punched(0, getHP());
1312 ActiveObjectMessage aom(getId(), true, str);
1313 m_messages_out.push_back(aom);
1317 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1319 m_armor_groups = armor_groups;
1320 m_armor_groups_sent = false;
1323 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1325 // store these so they can be updated to clients
1326 m_animation_range = frame_range;
1327 m_animation_speed = frame_speed;
1328 m_animation_blend = frame_blend;
1329 m_animation_sent = false;
1332 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1334 // store these so they can be updated to clients
1335 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1336 m_bone_position_sent = false;
1339 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1341 // Attachments need to be handled on both the server and client.
1342 // If we just attach on the server, we can only copy the position of the parent. Attachments
1343 // are still sent to clients at an interval so players might see them lagging, plus we can't
1344 // read and attach to skeletal bones.
1345 // If we just attach on the client, the server still sees the child at its original location.
1346 // This breaks some things so we also give the server the most accurate representation
1347 // even if players only see the client changes.
1349 m_attachment_parent_id = parent_id;
1350 m_attachment_bone = bone;
1351 m_attachment_position = position;
1352 m_attachment_rotation = rotation;
1353 m_attachment_sent = false;
1356 ObjectProperties* PlayerSAO::accessObjectProperties()
1361 void PlayerSAO::notifyObjectPropertiesModified()
1363 m_properties_sent = false;
1366 Inventory* PlayerSAO::getInventory()
1370 const Inventory* PlayerSAO::getInventory() const
1375 InventoryLocation PlayerSAO::getInventoryLocation() const
1377 InventoryLocation loc;
1378 loc.setPlayer(m_player->getName());
1382 void PlayerSAO::setInventoryModified()
1384 m_inventory_not_sent = true;
1387 std::string PlayerSAO::getWieldList() const
1392 int PlayerSAO::getWieldIndex() const
1394 return m_wield_index;
1397 void PlayerSAO::setWieldIndex(int i)
1399 if(i != m_wield_index)
1402 m_wielded_item_not_sent = true;
1406 void PlayerSAO::disconnected()
1410 if(m_player->getPlayerSAO() == this)
1412 m_player->setPlayerSAO(NULL);
1413 m_player->peer_id = 0;
1417 std::string PlayerSAO::getPropertyPacket()
1419 m_prop.is_visible = (true);
1420 return gob_cmd_set_properties(m_prop);