3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_sao.h"
21 #include "collision.h"
22 #include "environment.h"
24 #include "main.h" // For g_profiler
26 #include "serialization.h" // For compressZlib
27 #include "tool.h" // For ToolCapabilities
30 #include "scriptapi.h"
31 #include "genericobject.h"
32 #include "util/serialize.h"
34 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
40 class DummyLoadSAO : public ServerActiveObject
43 DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
44 ServerActiveObject(env, pos)
46 ServerActiveObject::registerType(type, create);
48 // Pretend to be the test object (to fool the client)
50 { return ACTIVEOBJECT_TYPE_TEST; }
51 // And never save to disk
52 bool isStaticAllowed() const
55 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
56 const std::string &data)
58 return new DummyLoadSAO(env, pos, 0);
61 void step(float dtime, bool send_recommended)
64 infostream<<"DummyLoadSAO step"<<std::endl;
70 // Prototype (registers item for deserialization)
71 DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
72 DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
73 DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
74 DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
80 class TestSAO : public ServerActiveObject
83 TestSAO(ServerEnvironment *env, v3f pos):
84 ServerActiveObject(env, pos),
88 ServerActiveObject::registerType(getType(), create);
91 { return ACTIVEOBJECT_TYPE_TEST; }
93 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
94 const std::string &data)
96 return new TestSAO(env, pos);
99 void step(float dtime, bool send_recommended)
108 m_base_position.Y += dtime * BS * 2;
109 if(m_base_position.Y > 8*BS)
110 m_base_position.Y = 2*BS;
112 if(send_recommended == false)
122 data += itos(0); // 0 = position
124 data += itos(m_base_position.X);
126 data += itos(m_base_position.Y);
128 data += itos(m_base_position.Z);
130 ActiveObjectMessage aom(getId(), false, data);
131 m_messages_out.push_back(aom);
140 // Prototype (registers item for deserialization)
141 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
146 DEPRECATED: New dropped items are implemented in Lua; see
147 builtin/item_entity.lua.
150 class ItemSAO : public ServerActiveObject
154 { return ACTIVEOBJECT_TYPE_ITEM; }
156 float getMinimumSavedMovement()
159 static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
160 const std::string &data)
162 std::istringstream is(data, std::ios::binary);
167 // check if version is supported
170 std::string itemstring = deSerializeString(is);
171 infostream<<"create(): Creating item \""
172 <<itemstring<<"\""<<std::endl;
173 return new ItemSAO(env, pos, itemstring);
176 ItemSAO(ServerEnvironment *env, v3f pos,
177 const std::string itemstring):
178 ServerActiveObject(env, pos),
179 m_itemstring(itemstring),
180 m_itemstring_changed(false),
182 m_last_sent_position(0,0,0)
184 ServerActiveObject::registerType(getType(), create);
187 void step(float dtime, bool send_recommended)
189 ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
193 const float interval = 0.2;
194 if(m_move_interval.step(dtime, interval)==false)
198 core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
199 collisionMoveResult moveresult;
201 m_speed_f += v3f(0, -dtime*9.81*BS, 0);
202 // Maximum movement without glitches
203 f32 pos_max_d = BS*0.25;
205 if(m_speed_f.getLength()*dtime > pos_max_d)
206 m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
207 v3f pos_f = getBasePosition();
208 v3f pos_f_old = pos_f;
209 v3f accel_f = v3f(0,0,0);
211 IGameDef *gamedef = m_env->getGameDef();
212 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
213 pos_max_d, box, stepheight, dtime,
214 pos_f, m_speed_f, accel_f);
216 if(send_recommended == false)
219 if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
221 setBasePosition(pos_f);
222 m_last_sent_position = pos_f;
224 std::ostringstream os(std::ios::binary);
225 // command (0 = update position)
228 writeV3F1000(os, m_base_position);
229 // create message and add to list
230 ActiveObjectMessage aom(getId(), false, os.str());
231 m_messages_out.push_back(aom);
233 if(m_itemstring_changed)
235 m_itemstring_changed = false;
237 std::ostringstream os(std::ios::binary);
238 // command (1 = update itemstring)
241 os<<serializeString(m_itemstring);
242 // create message and add to list
243 ActiveObjectMessage aom(getId(), false, os.str());
244 m_messages_out.push_back(aom);
248 std::string getClientInitializationData(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_sent(false),
360 m_bone_position_sent(false),
361 m_attachment_sent(false)
363 // Only register type if no environment supplied
365 ServerActiveObject::registerType(getType(), create);
369 // Initialize something to armor groups
370 m_armor_groups["fleshy"] = 3;
371 m_armor_groups["snappy"] = 2;
374 LuaEntitySAO::~LuaEntitySAO()
377 lua_State *L = m_env->getLua();
378 scriptapi_luaentity_rm(L, m_id);
382 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
384 ServerActiveObject::addedToEnvironment(dtime_s);
386 // Create entity from name
387 lua_State *L = m_env->getLua();
388 m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str());
392 scriptapi_luaentity_get_properties(L, m_id, &m_prop);
393 // Initialize HP from properties
394 m_hp = m_prop.hp_max;
395 // Activate entity, supplying serialized state
396 scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
400 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
401 const std::string &data)
409 std::istringstream is(data, std::ios::binary);
411 u8 version = readU8(is);
412 // check if version is supported
414 name = deSerializeString(is);
415 state = deSerializeLongString(is);
417 else if(version == 1){
418 name = deSerializeString(is);
419 state = deSerializeLongString(is);
421 velocity = readV3F1000(is);
426 infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
427 <<state<<"\")"<<std::endl;
428 LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
430 sao->m_velocity = velocity;
435 bool LuaEntitySAO::isAttached()
437 if(!m_attachment_parent_id)
439 // Check if the parent still exists
440 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
446 void LuaEntitySAO::step(float dtime, bool send_recommended)
448 if(!m_properties_sent)
450 m_properties_sent = true;
451 std::string str = getPropertyPacket();
452 // create message and add to list
453 ActiveObjectMessage aom(getId(), true, str);
454 m_messages_out.push_back(aom);
457 // If attached, check that our parent is still there. If it isn't, detach.
458 if(m_attachment_parent_id && !isAttached())
460 m_attachment_parent_id = 0;
461 m_attachment_bone = "";
462 m_attachment_position = v3f(0,0,0);
463 m_attachment_rotation = v3f(0,0,0);
464 sendPosition(false, true);
467 m_last_sent_position_timer += dtime;
469 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
470 // If the object gets detached this comes into effect automatically from the last known origin
473 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
474 m_base_position = pos;
475 m_velocity = v3f(0,0,0);
476 m_acceleration = v3f(0,0,0);
481 core::aabbox3d<f32> box = m_prop.collisionbox;
484 collisionMoveResult moveresult;
485 f32 pos_max_d = BS*0.25; // Distance per iteration
486 f32 stepheight = 0; // Maximum climbable step height
487 v3f p_pos = m_base_position;
488 v3f p_velocity = m_velocity;
489 v3f p_acceleration = m_acceleration;
490 IGameDef *gamedef = m_env->getGameDef();
491 moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
492 pos_max_d, box, stepheight, dtime,
493 p_pos, p_velocity, p_acceleration);
495 m_base_position = p_pos;
496 m_velocity = p_velocity;
497 m_acceleration = p_acceleration;
499 m_base_position += dtime * m_velocity + 0.5 * dtime
500 * dtime * m_acceleration;
501 m_velocity += dtime * m_acceleration;
506 lua_State *L = m_env->getLua();
507 scriptapi_luaentity_step(L, m_id, dtime);
510 if(send_recommended == false)
515 // TODO: force send when acceleration changes enough?
516 float minchange = 0.2*BS;
517 if(m_last_sent_position_timer > 1.0){
519 } else if(m_last_sent_position_timer > 0.2){
522 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
523 move_d += m_last_sent_move_precision;
524 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
525 if(move_d > minchange || vel_d > minchange ||
526 fabs(m_yaw - m_last_sent_yaw) > 1.0){
527 sendPosition(true, false);
531 if(m_armor_groups_sent == false){
532 m_armor_groups_sent = true;
533 std::string str = gob_cmd_update_armor_groups(
535 // create message and add to list
536 ActiveObjectMessage aom(getId(), true, str);
537 m_messages_out.push_back(aom);
540 if(m_animation_sent == false){
541 m_animation_sent = true;
542 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
543 // create message and add to list
544 ActiveObjectMessage aom(getId(), true, str);
545 m_messages_out.push_back(aom);
548 if(m_bone_position_sent == false){
549 m_bone_position_sent = true;
550 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
551 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
552 // create message and add to list
553 ActiveObjectMessage aom(getId(), true, str);
554 m_messages_out.push_back(aom);
558 if(m_attachment_sent == false){
559 m_attachment_sent = true;
560 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
561 // create message and add to list
562 ActiveObjectMessage aom(getId(), true, str);
563 m_messages_out.push_back(aom);
567 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
569 std::ostringstream os(std::ios::binary);
571 if(protocol_version >= 14)
573 writeU8(os, 1); // version
574 os<<serializeString(""); // name
575 writeU8(os, 0); // is_player
576 writeS16(os, getId()); //id
577 writeV3F1000(os, m_base_position);
578 writeF1000(os, m_yaw);
581 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
582 os<<serializeLongString(getPropertyPacket()); // message 1
583 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
584 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
585 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
586 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
588 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
592 writeU8(os, 0); // version
593 os<<serializeString(""); // name
594 writeU8(os, 0); // is_player
595 writeV3F1000(os, m_base_position);
596 writeF1000(os, m_yaw);
598 writeU8(os, 2); // number of messages stuffed in here
599 os<<serializeLongString(getPropertyPacket()); // message 1
600 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
607 std::string LuaEntitySAO::getStaticData()
609 verbosestream<<__FUNCTION_NAME<<std::endl;
610 std::ostringstream os(std::ios::binary);
614 os<<serializeString(m_init_name);
617 lua_State *L = m_env->getLua();
618 std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
619 os<<serializeLongString(state);
621 os<<serializeLongString(m_init_state);
626 writeV3F1000(os, m_velocity);
628 writeF1000(os, m_yaw);
632 int LuaEntitySAO::punch(v3f dir,
633 const ToolCapabilities *toolcap,
634 ServerActiveObject *puncher,
635 float time_from_last_punch)
638 // Delete unknown LuaEntities when punched
643 // It's best that attachments cannot be punched
647 ItemStack *punchitem = NULL;
648 ItemStack punchitem_static;
650 punchitem_static = puncher->getWieldedItem();
651 punchitem = &punchitem_static;
654 PunchDamageResult result = getPunchDamage(
658 time_from_last_punch);
662 setHP(getHP() - result.damage);
664 actionstream<<getDescription()<<" punched by "
665 <<puncher->getDescription()<<", damage "<<result.damage
666 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
669 std::string str = gob_cmd_punched(result.damage, getHP());
670 // create message and add to list
671 ActiveObjectMessage aom(getId(), true, str);
672 m_messages_out.push_back(aom);
679 lua_State *L = m_env->getLua();
680 scriptapi_luaentity_punch(L, m_id, puncher,
681 time_from_last_punch, toolcap, dir);
686 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
690 // It's best that attachments cannot be clicked
693 lua_State *L = m_env->getLua();
694 scriptapi_luaentity_rightclick(L, m_id, clicker);
697 void LuaEntitySAO::setPos(v3f pos)
701 m_base_position = pos;
702 sendPosition(false, true);
705 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
709 m_base_position = pos;
711 sendPosition(true, true);
714 float LuaEntitySAO::getMinimumSavedMovement()
719 std::string LuaEntitySAO::getDescription()
721 std::ostringstream os(std::ios::binary);
722 os<<"LuaEntitySAO at (";
723 os<<(m_base_position.X/BS)<<",";
724 os<<(m_base_position.Y/BS)<<",";
725 os<<(m_base_position.Z/BS);
730 void LuaEntitySAO::setHP(s16 hp)
736 s16 LuaEntitySAO::getHP() const
741 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
743 m_armor_groups = armor_groups;
744 m_armor_groups_sent = false;
747 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
749 m_animation_range = frame_range;
750 m_animation_speed = frame_speed;
751 m_animation_blend = frame_blend;
752 m_animation_sent = false;
755 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
757 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
758 m_bone_position_sent = false;
761 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
763 // Attachments need to be handled on both the server and client.
764 // If we just attach on the server, we can only copy the position of the parent. Attachments
765 // are still sent to clients at an interval so players might see them lagging, plus we can't
766 // read and attach to skeletal bones.
767 // If we just attach on the client, the server still sees the child at its original location.
768 // This breaks some things so we also give the server the most accurate representation
769 // even if players only see the client changes.
771 m_attachment_parent_id = parent_id;
772 m_attachment_bone = bone;
773 m_attachment_position = position;
774 m_attachment_rotation = rotation;
775 m_attachment_sent = false;
778 ObjectProperties* LuaEntitySAO::accessObjectProperties()
783 void LuaEntitySAO::notifyObjectPropertiesModified()
785 m_properties_sent = false;
788 void LuaEntitySAO::setVelocity(v3f velocity)
790 m_velocity = velocity;
793 v3f LuaEntitySAO::getVelocity()
798 void LuaEntitySAO::setAcceleration(v3f acceleration)
800 m_acceleration = acceleration;
803 v3f LuaEntitySAO::getAcceleration()
805 return m_acceleration;
808 void LuaEntitySAO::setYaw(float yaw)
813 float LuaEntitySAO::getYaw()
818 void LuaEntitySAO::setTextureMod(const std::string &mod)
820 std::string str = gob_cmd_set_texture_mod(mod);
821 // create message and add to list
822 ActiveObjectMessage aom(getId(), true, str);
823 m_messages_out.push_back(aom);
826 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
827 bool select_horiz_by_yawpitch)
829 std::string str = gob_cmd_set_sprite(
833 select_horiz_by_yawpitch
835 // create message and add to list
836 ActiveObjectMessage aom(getId(), true, str);
837 m_messages_out.push_back(aom);
840 std::string LuaEntitySAO::getName()
845 std::string LuaEntitySAO::getPropertyPacket()
847 return gob_cmd_set_properties(m_prop);
850 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
852 // If the object is attached client-side, don't waste bandwidth sending its position to clients
856 m_last_sent_move_precision = m_base_position.getDistanceFrom(
857 m_last_sent_position);
858 m_last_sent_position_timer = 0;
859 m_last_sent_yaw = m_yaw;
860 m_last_sent_position = m_base_position;
861 m_last_sent_velocity = m_velocity;
862 //m_last_sent_acceleration = m_acceleration;
864 float update_interval = m_env->getSendRecommendedInterval();
866 std::string str = gob_cmd_update_position(
875 // create message and add to list
876 ActiveObjectMessage aom(getId(), false, str);
877 m_messages_out.push_back(aom);
884 // No prototype, PlayerSAO does not need to be deserialized
886 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
887 const std::set<std::string> &privs, bool is_singleplayer):
888 ServerActiveObject(env_, v3f(0,0,0)),
892 m_last_good_position(0,0,0),
893 m_last_good_position_age(0),
894 m_time_from_last_punch(0),
895 m_nocheat_dig_pos(32767, 32767, 32767),
896 m_nocheat_dig_time(0),
898 m_position_not_sent(false),
899 m_armor_groups_sent(false),
900 m_properties_sent(true),
902 m_is_singleplayer(is_singleplayer),
903 m_animation_sent(false),
904 m_bone_position_sent(false),
905 m_attachment_sent(false),
908 m_inventory_not_sent(false),
909 m_hp_not_sent(false),
910 m_wielded_item_not_sent(false)
913 assert(m_peer_id != 0);
914 setBasePosition(m_player->getPosition());
915 m_inventory = &m_player->inventory;
916 m_armor_groups["choppy"] = 2;
917 m_armor_groups["fleshy"] = 3;
919 m_prop.hp_max = PLAYER_MAX_HP;
920 m_prop.physical = false;
922 m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
923 // start of default appearance, this should be overwritten by LUA
924 m_prop.visual = "upright_sprite";
925 m_prop.visual_size = v2f(1, 2);
926 m_prop.textures.clear();
927 m_prop.textures.push_back("player.png");
928 m_prop.textures.push_back("player_back.png");
929 m_prop.colors.clear();
930 m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
931 m_prop.spritediv = v2s16(1,1);
932 // end of default appearance
933 m_prop.is_visible = true;
934 m_prop.makes_footstep_sound = true;
937 PlayerSAO::~PlayerSAO()
939 if(m_inventory != &m_player->inventory)
944 std::string PlayerSAO::getDescription()
946 return std::string("player ") + m_player->getName();
949 // Called after id has been set and has been inserted in environment
950 void PlayerSAO::addedToEnvironment(u32 dtime_s)
952 ServerActiveObject::addedToEnvironment(dtime_s);
953 ServerActiveObject::setBasePosition(m_player->getPosition());
954 m_player->setPlayerSAO(this);
955 m_player->peer_id = m_peer_id;
956 m_last_good_position = m_player->getPosition();
957 m_last_good_position_age = 0.0;
960 // Called before removing from environment
961 void PlayerSAO::removingFromEnvironment()
963 ServerActiveObject::removingFromEnvironment();
964 if(m_player->getPlayerSAO() == this)
966 m_player->setPlayerSAO(NULL);
967 m_player->peer_id = 0;
971 bool PlayerSAO::isStaticAllowed() const
976 bool PlayerSAO::unlimitedTransferDistance() const
978 return g_settings->getBool("unlimited_player_transfer_distance");
981 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
983 std::ostringstream os(std::ios::binary);
985 if(protocol_version >= 15)
987 writeU8(os, 1); // version
988 os<<serializeString(m_player->getName()); // name
989 writeU8(os, 1); // is_player
990 writeS16(os, getId()); //id
991 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
992 writeF1000(os, m_player->getYaw());
993 writeS16(os, getHP());
995 writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
996 os<<serializeLongString(getPropertyPacket()); // message 1
997 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
998 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
999 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1000 os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
1002 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
1006 writeU8(os, 0); // version
1007 os<<serializeString(m_player->getName()); // name
1008 writeU8(os, 1); // is_player
1009 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
1010 writeF1000(os, m_player->getYaw());
1011 writeS16(os, getHP());
1012 writeU8(os, 2); // number of messages stuffed in here
1013 os<<serializeLongString(getPropertyPacket()); // message 1
1014 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
1021 std::string PlayerSAO::getStaticData()
1027 bool PlayerSAO::isAttached()
1029 if(!m_attachment_parent_id)
1031 // Check if the parent still exists
1032 ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
1038 void PlayerSAO::step(float dtime, bool send_recommended)
1040 if(!m_properties_sent)
1042 m_properties_sent = true;
1043 std::string str = getPropertyPacket();
1044 // create message and add to list
1045 ActiveObjectMessage aom(getId(), true, str);
1046 m_messages_out.push_back(aom);
1049 // If attached, check that our parent is still there. If it isn't, detach.
1050 if(m_attachment_parent_id && !isAttached())
1052 m_attachment_parent_id = 0;
1053 m_attachment_bone = "";
1054 m_attachment_position = v3f(0,0,0);
1055 m_attachment_rotation = v3f(0,0,0);
1056 m_player->setPosition(m_last_good_position);
1060 m_time_from_last_punch += dtime;
1061 m_nocheat_dig_time += dtime;
1063 // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
1064 // If the object gets detached this comes into effect automatically from the last known origin
1067 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1068 m_last_good_position = pos;
1069 m_last_good_position_age = 0;
1070 m_player->setPosition(pos);
1074 if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
1076 m_last_good_position = m_player->getPosition();
1077 m_last_good_position_age = 0;
1082 Check player movements
1084 NOTE: Actually the server should handle player physics like the
1085 client does and compare player's position to what is calculated
1086 on our side. This is required when eg. players fly due to an
1087 explosion. Altough a node-based alternative might be possible
1088 too, and much more lightweight.
1091 float player_max_speed = 0;
1092 float player_max_speed_up = 0;
1093 if(m_privs.count("fast") != 0){
1095 player_max_speed = BS * 20;
1096 player_max_speed_up = BS * 20;
1099 player_max_speed = BS * 4.0;
1100 player_max_speed_up = BS * 4.0;
1103 player_max_speed *= 2.5;
1104 player_max_speed_up *= 2.5;
1106 m_last_good_position_age += dtime;
1107 if(m_last_good_position_age >= 1.0){
1108 float age = m_last_good_position_age;
1109 v3f diff = (m_player->getPosition() - m_last_good_position);
1110 float d_vert = diff.Y;
1112 float d_horiz = diff.getLength();
1113 /*infostream<<m_player->getName()<<"'s horizontal speed is "
1114 <<(d_horiz/age)<<std::endl;*/
1115 if(d_horiz <= age * player_max_speed &&
1116 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1117 m_last_good_position = m_player->getPosition();
1119 actionstream<<"Player "<<m_player->getName()
1120 <<" moved too fast; resetting position"
1122 m_player->setPosition(m_last_good_position);
1125 m_last_good_position_age = 0;
1130 if(send_recommended == false)
1133 // If the object is attached client-side, don't waste bandwidth sending its position to clients
1134 if(m_position_not_sent && !isAttached())
1136 m_position_not_sent = false;
1137 float update_interval = m_env->getSendRecommendedInterval();
1139 if(isAttached()) // Just in case we ever do send attachment position too
1140 pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1142 pos = m_player->getPosition() + v3f(0,BS*1,0);
1143 std::string str = gob_cmd_update_position(
1152 // create message and add to list
1153 ActiveObjectMessage aom(getId(), false, str);
1154 m_messages_out.push_back(aom);
1157 if(m_wielded_item_not_sent)
1159 m_wielded_item_not_sent = false;
1160 // GenericCAO has no special way to show this
1163 if(m_armor_groups_sent == false){
1164 m_armor_groups_sent = true;
1165 std::string str = gob_cmd_update_armor_groups(
1167 // create message and add to list
1168 ActiveObjectMessage aom(getId(), true, str);
1169 m_messages_out.push_back(aom);
1172 if(m_animation_sent == false){
1173 m_animation_sent = true;
1174 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
1175 // create message and add to list
1176 ActiveObjectMessage aom(getId(), true, str);
1177 m_messages_out.push_back(aom);
1180 if(m_bone_position_sent == false){
1181 m_bone_position_sent = true;
1182 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1183 std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1184 // create message and add to list
1185 ActiveObjectMessage aom(getId(), true, str);
1186 m_messages_out.push_back(aom);
1190 if(m_attachment_sent == false){
1191 m_attachment_sent = true;
1192 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1193 // create message and add to list
1194 ActiveObjectMessage aom(getId(), true, str);
1195 m_messages_out.push_back(aom);
1199 void PlayerSAO::setBasePosition(const v3f &position)
1201 // This needs to be ran for attachments too
1202 ServerActiveObject::setBasePosition(position);
1203 m_position_not_sent = true;
1206 void PlayerSAO::setPos(v3f pos)
1210 m_player->setPosition(pos);
1211 // Movement caused by this command is always valid
1212 m_last_good_position = pos;
1213 m_last_good_position_age = 0;
1214 // Force position change on client
1218 void PlayerSAO::moveTo(v3f pos, bool continuous)
1222 m_player->setPosition(pos);
1223 // Movement caused by this command is always valid
1224 m_last_good_position = pos;
1225 m_last_good_position_age = 0;
1226 // Force position change on client
1230 int PlayerSAO::punch(v3f dir,
1231 const ToolCapabilities *toolcap,
1232 ServerActiveObject *puncher,
1233 float time_from_last_punch)
1235 // It's best that attachments cannot be punched
1242 // No effect if PvP disabled
1243 if(g_settings->getBool("enable_pvp") == false){
1244 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1245 std::string str = gob_cmd_punched(0, getHP());
1246 // create message and add to list
1247 ActiveObjectMessage aom(getId(), true, str);
1248 m_messages_out.push_back(aom);
1253 HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1254 time_from_last_punch);
1256 actionstream<<"Player "<<m_player->getName()<<" punched by "
1257 <<puncher->getDescription()<<", damage "<<hitparams.hp
1260 setHP(getHP() - hitparams.hp);
1262 if(hitparams.hp != 0)
1264 std::string str = gob_cmd_punched(hitparams.hp, getHP());
1265 // create message and add to list
1266 ActiveObjectMessage aom(getId(), true, str);
1267 m_messages_out.push_back(aom);
1270 return hitparams.wear;
1273 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1277 s16 PlayerSAO::getHP() const
1279 return m_player->hp;
1282 void PlayerSAO::setHP(s16 hp)
1284 s16 oldhp = m_player->hp;
1288 else if(hp > PLAYER_MAX_HP)
1291 if(hp < oldhp && g_settings->getBool("enable_damage") == false)
1293 m_hp_not_sent = true; // fix wrong prediction on client
1300 m_hp_not_sent = true;
1302 // On death or reincarnation send an active object message
1303 if((hp == 0) != (oldhp == 0))
1305 // Will send new is_visible value based on (getHP()!=0)
1306 m_properties_sent = false;
1308 std::string str = gob_cmd_punched(0, getHP());
1309 ActiveObjectMessage aom(getId(), true, str);
1310 m_messages_out.push_back(aom);
1314 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1316 m_armor_groups = armor_groups;
1317 m_armor_groups_sent = false;
1320 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1322 // store these so they can be updated to clients
1323 m_animation_range = frame_range;
1324 m_animation_speed = frame_speed;
1325 m_animation_blend = frame_blend;
1326 m_animation_sent = false;
1329 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1331 // store these so they can be updated to clients
1332 m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1333 m_bone_position_sent = false;
1336 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1338 // Attachments need to be handled on both the server and client.
1339 // If we just attach on the server, we can only copy the position of the parent. Attachments
1340 // are still sent to clients at an interval so players might see them lagging, plus we can't
1341 // read and attach to skeletal bones.
1342 // If we just attach on the client, the server still sees the child at its original location.
1343 // This breaks some things so we also give the server the most accurate representation
1344 // even if players only see the client changes.
1346 m_attachment_parent_id = parent_id;
1347 m_attachment_bone = bone;
1348 m_attachment_position = position;
1349 m_attachment_rotation = rotation;
1350 m_attachment_sent = false;
1353 ObjectProperties* PlayerSAO::accessObjectProperties()
1358 void PlayerSAO::notifyObjectPropertiesModified()
1360 m_properties_sent = false;
1363 Inventory* PlayerSAO::getInventory()
1367 const Inventory* PlayerSAO::getInventory() const
1372 InventoryLocation PlayerSAO::getInventoryLocation() const
1374 InventoryLocation loc;
1375 loc.setPlayer(m_player->getName());
1379 void PlayerSAO::setInventoryModified()
1381 m_inventory_not_sent = true;
1384 std::string PlayerSAO::getWieldList() const
1389 int PlayerSAO::getWieldIndex() const
1391 return m_wield_index;
1394 void PlayerSAO::setWieldIndex(int i)
1396 if(i != m_wield_index)
1399 m_wielded_item_not_sent = true;
1403 void PlayerSAO::disconnected()
1407 if(m_player->getPlayerSAO() == this)
1409 m_player->setPlayerSAO(NULL);
1410 m_player->peer_id = 0;
1414 std::string PlayerSAO::getPropertyPacket()
1416 m_prop.is_visible = (true);
1417 return gob_cmd_set_properties(m_prop);